21 Commits

Author SHA1 Message Date
3008d8037d _unbox
Some checks failed
Build Android / Game (Android, Release ARM64) (push) Has been cancelled
Build iOS / Game (iOS, Release ARM64) (push) Has been cancelled
Build Linux / Editor (Linux, Development x64) (push) Has been cancelled
Build Linux / Game (Linux, Release x64) (push) Has been cancelled
Build macOS / Editor (Mac, Development ARM64) (push) Has been cancelled
Build macOS / Game (Mac, Release ARM64) (push) Has been cancelled
Build Windows / Editor (Windows, Development x64) (push) Has been cancelled
Build Windows / Game (Windows, Release x64) (push) Has been cancelled
Cooker / Cook (Mac) (push) Has been cancelled
Tests / Tests (Linux) (push) Has been cancelled
Tests / Tests (Windows) (push) Has been cancelled
2025-12-21 20:17:24 +02:00
0973363c64 wip 2 no leaks 2025-12-21 17:45:46 +02:00
5d45b9ea1c Fix managed boolean array conversion to native array 2025-12-20 01:51:37 +02:00
c40f7c12f2 Fix incorrect class namespace in bindings class name lookups 2025-12-19 23:57:31 +02:00
1b2d6372b2 Fix warnings 2025-12-19 13:16:35 +02:00
0782ea889c Fix command-line parsing for SDL CreateProcess arguments 2025-12-19 13:16:35 +02:00
ef89501111 Add function for parsing command-line arguments into argument list 2025-12-19 13:16:35 +02:00
608353b996 _managed handle pool start 2025-12-19 13:14:11 +02:00
221325ef09 _freemanaged dllexport used 2025-12-19 13:14:10 +02:00
8f57c91a9e _track disabled 2025-12-19 13:14:10 +02:00
968de34cae _wip 2025-12-19 13:14:10 +02:00
6586a98f8d Ensure managed converter functions are exported with optimizations 2025-12-19 13:14:10 +02:00
b3510b0e44 Add USED attribute for forcing compiler to emit the symbol 2025-12-19 13:14:10 +02:00
d5a92c1942 Fix missing exports for managed converter structures 2025-12-19 13:14:10 +02:00
417f82826f Expose RenderContext to scripting API 2025-12-19 13:14:10 +02:00
63bed0986a Fix clang bindings code generation for non-const ref parameters 2025-12-19 13:14:10 +02:00
c40eefc79d Fix compilation errors with /permissive- standard conformance mode 2025-12-19 13:14:10 +02:00
d68631dd20 Fix Editor project file generation to use custom code editor arguments 2025-12-19 13:14:10 +02:00
e77b772010 Get code editor name through CodeEditingManager 2025-12-19 13:14:10 +02:00
e41e956386 Fix default code editor not using Visual Studio as fallback editor 2025-12-19 13:14:10 +02:00
33e47c646b Fix invalid code editor when selected editor is not detected 2025-12-19 13:14:10 +02:00
46 changed files with 2050 additions and 373 deletions

View File

@@ -15,7 +15,7 @@ if errorlevel 1 goto BuildToolFailed
:: Build bindings for all editor configurations :: Build bindings for all editor configurations
echo Building C# bindings... echo Building C# bindings...
Binaries\Tools\Flax.Build.exe -build -BuildBindingsOnly -arch=x64 -platform=Windows --buildTargets=FlaxEditor Binaries\Tools\Flax.Build.exe -build -BuildBindingsOnly -arch=x64 -platform=Windows --buildTargets=FlaxEditor,FlaxGame
popd popd
echo Done! echo Done!

View File

@@ -334,7 +334,7 @@ Window* ManagedEditor::GetMainWindow()
ASSERT(HasManagedInstance()); ASSERT(HasManagedInstance());
const auto method = GetClass()->GetMethod("GetMainWindowPtr"); const auto method = GetClass()->GetMethod("GetMainWindowPtr");
ASSERT(method); ASSERT(method);
return (Window*)MUtils::Unbox<void*>(method->Invoke(GetManagedInstance(), nullptr, nullptr)); return (Window*)MUtils::Unbox<void*>(method->Invoke(GetManagedInstance(), nullptr, nullptr), true);
} }
bool ManagedEditor::CanReloadScripts() bool ManagedEditor::CanReloadScripts()
@@ -346,7 +346,7 @@ bool ManagedEditor::CanReloadScripts()
Internal_CanReloadScripts = GetClass()->GetMethod("Internal_CanReloadScripts"); Internal_CanReloadScripts = GetClass()->GetMethod("Internal_CanReloadScripts");
ASSERT(Internal_CanReloadScripts); ASSERT(Internal_CanReloadScripts);
} }
return MUtils::Unbox<bool>(Internal_CanReloadScripts->Invoke(GetManagedInstance(), nullptr, nullptr)); return MUtils::Unbox<bool>(Internal_CanReloadScripts->Invoke(GetManagedInstance(), nullptr, nullptr), true);
} }
bool ManagedEditor::CanAutoBuildCSG() bool ManagedEditor::CanAutoBuildCSG()
@@ -365,7 +365,7 @@ bool ManagedEditor::CanAutoBuildCSG()
Internal_CanAutoBuildCSG = GetClass()->GetMethod("Internal_CanAutoBuildCSG"); Internal_CanAutoBuildCSG = GetClass()->GetMethod("Internal_CanAutoBuildCSG");
ASSERT(Internal_CanAutoBuildCSG); ASSERT(Internal_CanAutoBuildCSG);
} }
return MUtils::Unbox<bool>(Internal_CanAutoBuildCSG->Invoke(GetManagedInstance(), nullptr, nullptr)); return MUtils::Unbox<bool>(Internal_CanAutoBuildCSG->Invoke(GetManagedInstance(), nullptr, nullptr), true);
} }
bool ManagedEditor::CanAutoBuildNavMesh() bool ManagedEditor::CanAutoBuildNavMesh()
@@ -384,7 +384,7 @@ bool ManagedEditor::CanAutoBuildNavMesh()
Internal_CanAutoBuildNavMesh = GetClass()->GetMethod("Internal_CanAutoBuildNavMesh"); Internal_CanAutoBuildNavMesh = GetClass()->GetMethod("Internal_CanAutoBuildNavMesh");
ASSERT(Internal_CanAutoBuildNavMesh); ASSERT(Internal_CanAutoBuildNavMesh);
} }
return MUtils::Unbox<bool>(Internal_CanAutoBuildNavMesh->Invoke(GetManagedInstance(), nullptr, nullptr)); return MUtils::Unbox<bool>(Internal_CanAutoBuildNavMesh->Invoke(GetManagedInstance(), nullptr, nullptr), true);
} }
bool ManagedEditor::HasGameViewportFocus() const bool ManagedEditor::HasGameViewportFocus() const
@@ -397,7 +397,8 @@ bool ManagedEditor::HasGameViewportFocus() const
Internal_HasGameViewportFocus = GetClass()->GetMethod("Internal_HasGameViewportFocus"); Internal_HasGameViewportFocus = GetClass()->GetMethod("Internal_HasGameViewportFocus");
ASSERT(Internal_HasGameViewportFocus); ASSERT(Internal_HasGameViewportFocus);
} }
result = MUtils::Unbox<bool>(Internal_HasGameViewportFocus->Invoke(GetManagedInstance(), nullptr, nullptr)); auto invk = Internal_HasGameViewportFocus->Invoke(GetManagedInstance(), nullptr, nullptr);
result = MUtils::Unbox<bool>(invk, true);
} }
return result; return result;
} }
@@ -495,7 +496,7 @@ bool ManagedEditor::OnAppExit()
Internal_OnAppExit = GetClass()->GetMethod("Internal_OnAppExit"); Internal_OnAppExit = GetClass()->GetMethod("Internal_OnAppExit");
ASSERT(Internal_OnAppExit); ASSERT(Internal_OnAppExit);
} }
return MUtils::Unbox<bool>(Internal_OnAppExit->Invoke(GetManagedInstance(), nullptr, nullptr)); return MUtils::Unbox<bool>(Internal_OnAppExit->Invoke(GetManagedInstance(), nullptr, nullptr), true);
} }
void ManagedEditor::RequestStartPlayOnEditMode() void ManagedEditor::RequestStartPlayOnEditMode()

View File

@@ -334,6 +334,9 @@ namespace FlaxEditor.Modules.SourceCodeEditing
} }
} }
} }
if (editor == null)
editor = Editor.Instance.CodeEditing.Editors[0];
Editor.Instance.CodeEditing.SelectedEditor = editor; Editor.Instance.CodeEditing.SelectedEditor = editor;
} }

View File

@@ -41,9 +41,9 @@ namespace FlaxEditor.Modules.SourceCodeEditing
var vsCode = codeEditing.GetInBuildEditor(CodeEditorTypes.VSCode); var vsCode = codeEditing.GetInBuildEditor(CodeEditorTypes.VSCode);
var rider = codeEditing.GetInBuildEditor(CodeEditorTypes.Rider); var rider = codeEditing.GetInBuildEditor(CodeEditorTypes.Rider);
#if PLATFORM_WINDOW #if PLATFORM_WINDOWS
// Favor the newest Visual Studio // Favor the newest Visual Studio
for (int i = (int)CodeEditorTypes.VS2019; i >= (int)CodeEditorTypes.VS2008; i--) for (int i = (int)CodeEditorTypes.VS2026; i >= (int)CodeEditorTypes.VS2008; i--)
{ {
var visualStudio = codeEditing.GetInBuildEditor((CodeEditorTypes)i); var visualStudio = codeEditing.GetInBuildEditor((CodeEditorTypes)i);
if (visualStudio != null) if (visualStudio != null)
@@ -74,7 +74,7 @@ namespace FlaxEditor.Modules.SourceCodeEditing
public string Name => "Default"; public string Name => "Default";
/// <inheritdoc /> /// <inheritdoc />
public string GenerateProjectCustomArgs => null; public string GenerateProjectCustomArgs => _currentEditor?.GenerateProjectCustomArgs;
/// <inheritdoc /> /// <inheritdoc />
public void OpenSolution() public void OpenSolution()

View File

@@ -22,71 +22,14 @@ namespace FlaxEditor.Modules.SourceCodeEditing
public InBuildSourceCodeEditor(CodeEditorTypes type) public InBuildSourceCodeEditor(CodeEditorTypes type)
{ {
Type = type; Type = type;
switch (type) Name = CodeEditingManager.GetName(type);
{
case CodeEditorTypes.Custom:
Name = "Custom";
break;
case CodeEditorTypes.SystemDefault:
Name = "System Default";
break;
case CodeEditorTypes.VS2008:
Name = "Visual Studio 2008";
break;
case CodeEditorTypes.VS2010:
Name = "Visual Studio 2010";
break;
case CodeEditorTypes.VS2012:
Name = "Visual Studio 2012";
break;
case CodeEditorTypes.VS2013:
Name = "Visual Studio 2013";
break;
case CodeEditorTypes.VS2015:
Name = "Visual Studio 2015";
break;
case CodeEditorTypes.VS2017:
Name = "Visual Studio 2017";
break;
case CodeEditorTypes.VS2019:
Name = "Visual Studio 2019";
break;
case CodeEditorTypes.VS2022:
Name = "Visual Studio 2022";
break;
case CodeEditorTypes.VS2026:
Name = "Visual Studio 2026";
break;
case CodeEditorTypes.VSCode:
Name = "Visual Studio Code";
break;
case CodeEditorTypes.VSCodeInsiders:
Name = "Visual Studio Code - Insiders";
break;
case CodeEditorTypes.Rider:
Name = "Rider";
break;
default: throw new ArgumentOutOfRangeException(nameof(type), type, null);
}
} }
/// <inheritdoc /> /// <inheritdoc />
public string Name { get; set; } public string Name { get; set; }
/// <inheritdoc /> /// <inheritdoc />
public string GenerateProjectCustomArgs public string GenerateProjectCustomArgs => CodeEditingManager.GetGenerateProjectCustomArgs(Type);
{
get
{
switch (Type)
{
case CodeEditorTypes.VSCodeInsiders:
case CodeEditorTypes.VSCode: return "-vscode -vs2022";
case CodeEditorTypes.Rider: return "-vs2022";
default: return null;
}
}
}
/// <inheritdoc /> /// <inheritdoc />
public void OpenSolution() public void OpenSolution()

View File

@@ -139,6 +139,34 @@ CodeEditor* CodeEditingManager::GetCodeEditor(CodeEditorTypes editorType)
return nullptr; return nullptr;
} }
String CodeEditingManager::GetName(CodeEditorTypes editorType)
{
const auto editor = GetCodeEditor(editorType);
if (editor)
{
return editor->GetName();
}
else
{
LOG(Warning, "Missing code editor type {0}", (int32)editorType);
return String::Empty;
}
}
String CodeEditingManager::GetGenerateProjectCustomArgs(CodeEditorTypes editorType)
{
const auto editor = GetCodeEditor(editorType);
if (editor)
{
return editor->GetGenerateProjectCustomArgs();
}
else
{
LOG(Warning, "Missing code editor type {0}", (int32)editorType);
return String::Empty;
}
}
void CodeEditingManager::OpenFile(CodeEditorTypes editorType, const String& path, int32 line) void CodeEditingManager::OpenFile(CodeEditorTypes editorType, const String& path, int32 line)
{ {
const auto editor = GetCodeEditor(editorType); const auto editor = GetCodeEditor(editorType);

View File

@@ -109,9 +109,18 @@ public:
/// <summary> /// <summary>
/// Gets the name of the editor. /// Gets the name of the editor.
/// </summary> /// </summary>
/// <returns>The name</returns> /// <returns>The name.</returns>
virtual String GetName() const = 0; virtual String GetName() const = 0;
/// <summary>
/// Gets the custom arguments for the Flax.Build tool to add when generating project files for this code editor.
/// </summary>
/// <returns>The custom arguments to generate project files.</returns>
virtual String GetGenerateProjectCustomArgs() const
{
return String::Empty;
}
/// <summary> /// <summary>
/// Opens the file. /// Opens the file.
/// </summary> /// </summary>
@@ -169,6 +178,20 @@ public:
/// <returns>The editor object or null if not found.</returns> /// <returns>The editor object or null if not found.</returns>
static CodeEditor* GetCodeEditor(CodeEditorTypes editorType); static CodeEditor* GetCodeEditor(CodeEditorTypes editorType);
/// <summary>
/// Gets the name of the editor.
/// </summary>
/// <param name="editorType">The code editor type.</param>
/// <returns>The name.</returns>
API_FUNCTION() static String GetName(CodeEditorTypes editorType);
/// <summary>
/// Gets the custom arguments for the Flax.Build tool to add when generating project files for this code editor.
/// </summary>
/// <param name="editorType">The code editor type.</param>
/// <returns>The custom arguments to generate project files.</returns>
API_FUNCTION() static String GetGenerateProjectCustomArgs(CodeEditorTypes editorType);
/// <summary> /// <summary>
/// Opens the file. Handles async opening. /// Opens the file. Handles async opening.
/// </summary> /// </summary>

View File

@@ -257,12 +257,17 @@ String RiderCodeEditor::GetName() const
return TEXT("Rider"); return TEXT("Rider");
} }
String RiderCodeEditor::GetGenerateProjectCustomArgs() const
{
return TEXT("-vs2022");
}
void RiderCodeEditor::OpenFile(const String& path, int32 line) void RiderCodeEditor::OpenFile(const String& path, int32 line)
{ {
// Generate project files if solution is missing // Generate project files if solution is missing
if (!FileSystem::FileExists(_solutionPath)) if (!FileSystem::FileExists(_solutionPath))
{ {
ScriptsBuilder::GenerateProject(TEXT("-vs2022")); ScriptsBuilder::GenerateProject(GetGenerateProjectCustomArgs());
} }
// Open file // Open file
@@ -290,7 +295,7 @@ void RiderCodeEditor::OpenSolution()
// Generate project files if solution is missing // Generate project files if solution is missing
if (!FileSystem::FileExists(_solutionPath)) if (!FileSystem::FileExists(_solutionPath))
{ {
ScriptsBuilder::GenerateProject(TEXT("-vs2022")); ScriptsBuilder::GenerateProject(GetGenerateProjectCustomArgs());
} }
// Open solution // Open solution
@@ -312,5 +317,5 @@ void RiderCodeEditor::OpenSolution()
void RiderCodeEditor::OnFileAdded(const String& path) void RiderCodeEditor::OnFileAdded(const String& path)
{ {
ScriptsBuilder::GenerateProject(); ScriptsBuilder::GenerateProject(GetGenerateProjectCustomArgs());
} }

View File

@@ -35,6 +35,7 @@ public:
// [CodeEditor] // [CodeEditor]
CodeEditorTypes GetType() const override; CodeEditorTypes GetType() const override;
String GetName() const override; String GetName() const override;
String GetGenerateProjectCustomArgs() const override;
void OpenFile(const String& path, int32 line) override; void OpenFile(const String& path, int32 line) override;
void OpenSolution() override; void OpenSolution() override;
void OnFileAdded(const String& path) override; void OnFileAdded(const String& path) override;

View File

@@ -148,12 +148,17 @@ String VisualStudioEditor::GetName() const
return String(ToString(_version)); return String(ToString(_version));
} }
String VisualStudioEditor::GetGenerateProjectCustomArgs() const
{
return String::Format(TEXT("-{0}"), String(ToString(_version)).ToLower());
}
void VisualStudioEditor::OpenFile(const String& path, int32 line) void VisualStudioEditor::OpenFile(const String& path, int32 line)
{ {
// Generate project files if solution is missing // Generate project files if solution is missing
if (!FileSystem::FileExists(_solutionPath)) if (!FileSystem::FileExists(_solutionPath))
{ {
ScriptsBuilder::GenerateProject(); ScriptsBuilder::GenerateProject(GetGenerateProjectCustomArgs());
} }
// Open file // Open file
@@ -172,7 +177,7 @@ void VisualStudioEditor::OpenSolution()
// Generate project files if solution is missing // Generate project files if solution is missing
if (!FileSystem::FileExists(_solutionPath)) if (!FileSystem::FileExists(_solutionPath))
{ {
ScriptsBuilder::GenerateProject(); ScriptsBuilder::GenerateProject(GetGenerateProjectCustomArgs());
} }
// Open solution // Open solution
@@ -187,7 +192,7 @@ void VisualStudioEditor::OpenSolution()
void VisualStudioEditor::OnFileAdded(const String& path) void VisualStudioEditor::OnFileAdded(const String& path)
{ {
// TODO: finish dynamic files adding to the project - for now just regenerate it // TODO: finish dynamic files adding to the project - for now just regenerate it
ScriptsBuilder::GenerateProject(); ScriptsBuilder::GenerateProject(GetGenerateProjectCustomArgs());
return; return;
if (!FileSystem::FileExists(_solutionPath)) if (!FileSystem::FileExists(_solutionPath))
{ {

View File

@@ -56,6 +56,7 @@ public:
// [CodeEditor] // [CodeEditor]
CodeEditorTypes GetType() const override; CodeEditorTypes GetType() const override;
String GetName() const override; String GetName() const override;
String GetGenerateProjectCustomArgs() const override;
void OpenFile(const String& path, int32 line) override; void OpenFile(const String& path, int32 line) override;
void OpenSolution() override; void OpenSolution() override;
void OnFileAdded(const String& path) override; void OnFileAdded(const String& path) override;

View File

@@ -128,6 +128,11 @@ String VisualStudioCodeEditor::GetName() const
return _isInsiders ? TEXT("Visual Studio Code - Insiders") : TEXT("Visual Studio Code"); return _isInsiders ? TEXT("Visual Studio Code - Insiders") : TEXT("Visual Studio Code");
} }
String VisualStudioCodeEditor::GetGenerateProjectCustomArgs() const
{
return TEXT("-vs2022 -vscode");
}
void VisualStudioCodeEditor::OpenFile(const String& path, int32 line) void VisualStudioCodeEditor::OpenFile(const String& path, int32 line)
{ {
// Generate VS solution files for intellisense // Generate VS solution files for intellisense

View File

@@ -37,6 +37,7 @@ public:
// [CodeEditor] // [CodeEditor]
CodeEditorTypes GetType() const override; CodeEditorTypes GetType() const override;
String GetName() const override; String GetName() const override;
String GetGenerateProjectCustomArgs() const override;
void OpenFile(const String& path, int32 line) override; void OpenFile(const String& path, int32 line) override;
void OpenSolution() override; void OpenSolution() override;
bool UseAsyncForOpen() const override; bool UseAsyncForOpen() const override;

View File

@@ -12,6 +12,7 @@ using FlaxEditor.GUI;
using FlaxEditor.GUI.ContextMenu; using FlaxEditor.GUI.ContextMenu;
using FlaxEditor.GUI.Tabs; using FlaxEditor.GUI.Tabs;
using FlaxEngine; using FlaxEngine;
using FlaxEngine.Assertions;
using FlaxEngine.GUI; using FlaxEngine.GUI;
using FlaxEngine.Json; using FlaxEngine.Json;
@@ -972,6 +973,9 @@ namespace FlaxEditor.Windows
_cloneProjectButton.Clicked -= OnCloneProjectButtonClicked; _cloneProjectButton.Clicked -= OnCloneProjectButtonClicked;
PluginManager.PluginsChanged -= OnPluginsChanged; PluginManager.PluginsChanged -= OnPluginsChanged;
Assert.IsTrue(!_entries.Any());
_entries.Clear();
base.OnDestroy(); base.OnDestroy();
} }
} }

View File

@@ -71,7 +71,7 @@ bool AccessVariant(Variant& instance, const StringAnsiView& member, Variant& val
if (set) if (set)
mField->SetValue(instanceObject, MUtils::VariantToManagedArgPtr(value, mField->GetType(), failed)); mField->SetValue(instanceObject, MUtils::VariantToManagedArgPtr(value, mField->GetType(), failed));
else else
value = MUtils::UnboxVariant(mField->GetValueBoxed(instanceObject)); value = MUtils::UnboxVariant(mField->GetValueBoxed(instanceObject), true);
return !failed; return !failed;
} }
else if (const auto mProperty = mClass->GetProperty(member.Get())) else if (const auto mProperty = mClass->GetProperty(member.Get()))
@@ -79,7 +79,7 @@ bool AccessVariant(Variant& instance, const StringAnsiView& member, Variant& val
if (set) if (set)
mProperty->SetValue(instanceObject, MUtils::VariantToManagedArgPtr(value, mProperty->GetType(), failed), nullptr); mProperty->SetValue(instanceObject, MUtils::VariantToManagedArgPtr(value, mProperty->GetType(), failed), nullptr);
else else
value = MUtils::UnboxVariant(mProperty->GetValue(instanceObject, nullptr)); value = MUtils::UnboxVariant(mProperty->GetValue(instanceObject, nullptr), true);
return !failed; return !failed;
} }
} }

View File

@@ -134,7 +134,7 @@ void AnimGraphExecutor::ProcessGroupCustom(Box* boxBase, Node* nodeBase, Value&
} }
// Extract result // Extract result
value = MUtils::UnboxVariant(result); value = MUtils::UnboxVariant(result, true);
context.ValueCache.Add(boxBase, value); context.ValueCache.Add(boxBase, value);
#endif #endif
} }

View File

@@ -962,7 +962,7 @@ void SceneAnimationPlayer::Tick(SceneAnimation* anim, float time, float dt, int3
else if (!MCore::Type::IsPointer(valueType) && !MCore::Type::IsReference(valueType)) else if (!MCore::Type::IsPointer(valueType) && !MCore::Type::IsReference(valueType))
{ {
if (boxed) if (boxed)
Platform::MemoryCopy(value, MCore::Object::Unbox(boxed), valueSize); MCore::Object::Unbox(boxed, value);
else else
Platform::MemoryClear(value, valueSize); Platform::MemoryClear(value, valueSize);
} }

View File

@@ -4,8 +4,9 @@
#if defined(__clang__) #if defined(__clang__)
#define DLLEXPORT __attribute__ ((__visibility__ ("default"))) #define DLLEXPORT __attribute__((__visibility__("default")))
#define DLLIMPORT #define DLLIMPORT
#define USED __attribute__((used))
#define THREADLOCAL __thread #define THREADLOCAL __thread
#define STDCALL __attribute__((stdcall)) #define STDCALL __attribute__((stdcall))
#define CDECL __attribute__((cdecl)) #define CDECL __attribute__((cdecl))
@@ -19,7 +20,7 @@
#define PACK_BEGIN() #define PACK_BEGIN()
#define PACK_END() __attribute__((__packed__)) #define PACK_END() __attribute__((__packed__))
#define ALIGN_BEGIN(_align) #define ALIGN_BEGIN(_align)
#define ALIGN_END(_align) __attribute__( (aligned(_align) ) ) #define ALIGN_END(_align) __attribute__((aligned(_align)))
#define OFFSET_OF(X, Y) __builtin_offsetof(X, Y) #define OFFSET_OF(X, Y) __builtin_offsetof(X, Y)
#define PRAGMA_DISABLE_DEPRECATION_WARNINGS \ #define PRAGMA_DISABLE_DEPRECATION_WARNINGS \
_Pragma("clang diagnostic push") \ _Pragma("clang diagnostic push") \
@@ -37,8 +38,9 @@
#elif defined(__GNUC__) #elif defined(__GNUC__)
#define DLLEXPORT __attribute__ ((__visibility__ ("default"))) #define DLLEXPORT __attribute__((__visibility__("default")))
#define DLLIMPORT #define DLLIMPORT
#define USED __attribute__((used))
#define THREADLOCAL __thread #define THREADLOCAL __thread
#define STDCALL __attribute__((stdcall)) #define STDCALL __attribute__((stdcall))
#define CDECL __attribute__((cdecl)) #define CDECL __attribute__((cdecl))
@@ -52,7 +54,7 @@
#define PACK_BEGIN() #define PACK_BEGIN()
#define PACK_END() __attribute__((__packed__)) #define PACK_END() __attribute__((__packed__))
#define ALIGN_BEGIN(_align) #define ALIGN_BEGIN(_align)
#define ALIGN_END(_align) __attribute__( (aligned(_align) ) ) #define ALIGN_END(_align) __attribute__((aligned(_align)))
#define OFFSET_OF(X, Y) __builtin_offsetof(X, Y) #define OFFSET_OF(X, Y) __builtin_offsetof(X, Y)
#define PRAGMA_DISABLE_DEPRECATION_WARNINGS #define PRAGMA_DISABLE_DEPRECATION_WARNINGS
#define PRAGMA_ENABLE_DEPRECATION_WARNINGS #define PRAGMA_ENABLE_DEPRECATION_WARNINGS
@@ -67,6 +69,7 @@
#define DLLEXPORT __declspec(dllexport) #define DLLEXPORT __declspec(dllexport)
#define DLLIMPORT __declspec(dllimport) #define DLLIMPORT __declspec(dllimport)
#define USED
#define THREADLOCAL __declspec(thread) #define THREADLOCAL __declspec(thread)
#define STDCALL __stdcall #define STDCALL __stdcall
#define CDECL __cdecl #define CDECL __cdecl

View File

@@ -4301,8 +4301,12 @@ void Variant::CopyStructure(void* src)
if (MANAGED_GC_HANDLE && mclass->IsValueType()) if (MANAGED_GC_HANDLE && mclass->IsValueType())
{ {
MObject* instance = MCore::GCHandle::GetTarget(MANAGED_GC_HANDLE); MObject* instance = MCore::GCHandle::GetTarget(MANAGED_GC_HANDLE);
void* data = MCore::Object::Unbox(instance); PLATFORM_DEBUG_BREAK; // FIXME
Platform::MemoryCopy(data, src, mclass->GetInstanceSize()); MCore::GCHandle::Free(MANAGED_GC_HANDLE);
instance = MCore::Object::Box(src, mclass);
MANAGED_GC_HANDLE = *(MGCHandle*)&instance;
//void* data = MCore::Object::Unbox(instance);
//Platform::MemoryCopy(data, src, mclass->GetInstanceSize());
} }
} }
#endif #endif

View File

@@ -3,6 +3,7 @@
#include "CommandLine.h" #include "CommandLine.h"
#include "Engine/Core/Collections/Array.h" #include "Engine/Core/Collections/Array.h"
#include "Engine/Core/Utilities.h" #include "Engine/Core/Utilities.h"
#include "Engine/Core/Types/StringView.h"
#include <iostream> #include <iostream>
CommandLine::OptionsData CommandLine::Options; CommandLine::OptionsData CommandLine::Options;
@@ -170,3 +171,63 @@ bool CommandLine::Parse(const Char* cmdLine)
return false; return false;
} }
bool CommandLine::ParseArguments(const StringView& cmdLine, Array<StringAnsi>& arguments)
{
int32 start = 0;
int32 quotesStart = -1;
int32 length = cmdLine.Length();
for (int32 i = 0; i < length; i++)
{
if (cmdLine[i] == ' ' && quotesStart == -1)
{
int32 count = i - start;
if (count > 0)
arguments.Add(StringAnsi(cmdLine.Substring(start, count)));
start = i + 1;
}
else if (cmdLine[i] == '\"')
{
if (quotesStart >= 0)
{
if (i + 1 < length && cmdLine[i + 1] != ' ')
{
// End quotes are in the middle of the current word,
// continue until the end of the current word.
}
else
{
int32 offset = 1;
if (quotesStart == start && cmdLine[start] == '\"')
{
// Word starts and ends with quotes, only include the quoted content.
quotesStart++;
offset--;
}
else if (quotesStart != start)
{
// Start quotes in the middle of the word, include the whole word.
quotesStart = start;
}
int32 count = i - quotesStart + offset;
if (count > 0)
arguments.Add(StringAnsi(cmdLine.Substring(quotesStart, count)));
start = i + 1;
}
quotesStart = -1;
}
else
{
quotesStart = i;
}
}
}
const int32 count = length - start;
if (count > 0)
arguments.Add(StringAnsi(cmdLine.Substring(start, count)));
if (quotesStart >= 0)
return true; // Missing last closing quote
return false;
}

View File

@@ -4,6 +4,7 @@
#include "Engine/Core/Types/String.h" #include "Engine/Core/Types/String.h"
#include "Engine/Core/Types/Nullable.h" #include "Engine/Core/Types/Nullable.h"
#include "Engine/Core/Collections/Array.h"
/// <summary> /// <summary>
/// Command line options helper. /// Command line options helper.
@@ -219,4 +220,12 @@ public:
/// <param name="cmdLine">The command line.</param> /// <param name="cmdLine">The command line.</param>
/// <returns>True if failed, otherwise false.</returns> /// <returns>True if failed, otherwise false.</returns>
static bool Parse(const Char* cmdLine); static bool Parse(const Char* cmdLine);
/// <summary>
/// Parses the command line arguments string into string list of arguments.
/// </summary>
/// <param name="cmdLine">The command line.</param>
/// <param name="arguments">The parsed arguments</param>
/// <returns>True if failed, otherwise false.</returns>
static bool ParseArguments(const StringView& cmdLine, Array<StringAnsi>& arguments);
}; };

View File

@@ -74,7 +74,7 @@ namespace FlaxEngine.Interop
internal static IntPtr MarshalReturnValueString(ref string returnValue) internal static IntPtr MarshalReturnValueString(ref string returnValue)
{ {
return returnValue != null ? ManagedString.ToNativeWeak(returnValue) : IntPtr.Zero; return returnValue != null ? ManagedString.ToNative/*Weak*/(returnValue) : IntPtr.Zero;
} }
internal static IntPtr MarshalReturnValueManagedHandle(ref ManagedHandle returnValue) internal static IntPtr MarshalReturnValueManagedHandle(ref ManagedHandle returnValue)
@@ -157,7 +157,7 @@ namespace FlaxEngine.Interop
if (returnObject == null) if (returnObject == null)
return IntPtr.Zero; return IntPtr.Zero;
if (returnType == typeof(string)) if (returnType == typeof(string))
return ManagedString.ToNativeWeak(Unsafe.As<string>(returnObject)); return ManagedString.ToNative/*Weak*/(Unsafe.As<string>(returnObject));
if (returnType == typeof(ManagedHandle)) if (returnType == typeof(ManagedHandle))
return ManagedHandle.ToIntPtr((ManagedHandle)(object)returnObject); return ManagedHandle.ToIntPtr((ManagedHandle)(object)returnObject);
if (returnType == typeof(bool)) if (returnType == typeof(bool))
@@ -168,7 +168,7 @@ namespace FlaxEngine.Interop
return ManagedHandle.ToIntPtr(ManagedArray.WrapNewArray(Unsafe.As<Array>(returnObject)), GCHandleType.Weak); return ManagedHandle.ToIntPtr(ManagedArray.WrapNewArray(Unsafe.As<Array>(returnObject)), GCHandleType.Weak);
if (returnType.IsArray) if (returnType.IsArray)
return ManagedHandle.ToIntPtr(ManagedArrayToGCHandleWrappedArray(Unsafe.As<Array>(returnObject)), GCHandleType.Weak); return ManagedHandle.ToIntPtr(ManagedArrayToGCHandleWrappedArray(Unsafe.As<Array>(returnObject)), GCHandleType.Weak);
return ManagedHandle.ToIntPtr(returnObject, GCHandleType.Weak); return ManagedHandle.ToIntPtr(returnObject/*, GCHandleType.Weak*/);
} }
internal static IntPtr MarshalReturnValueThunk<TRet>(ref TRet returnValue) internal static IntPtr MarshalReturnValueThunk<TRet>(ref TRet returnValue)
@@ -181,7 +181,7 @@ namespace FlaxEngine.Interop
if (returnObject == null) if (returnObject == null)
return IntPtr.Zero; return IntPtr.Zero;
if (returnType == typeof(string)) if (returnType == typeof(string))
return ManagedString.ToNativeWeak(Unsafe.As<string>(returnObject)); return ManagedString.ToNative/*Weak*/(Unsafe.As<string>(returnObject));
if (returnType == typeof(IntPtr)) if (returnType == typeof(IntPtr))
return (IntPtr)(object)returnObject; return (IntPtr)(object)returnObject;
if (returnType == typeof(ManagedHandle)) if (returnType == typeof(ManagedHandle))
@@ -210,7 +210,7 @@ namespace FlaxEngine.Interop
return (IntPtr)new UIntPtr((ulong)(System.UInt32)(object)returnObject); return (IntPtr)new UIntPtr((ulong)(System.UInt32)(object)returnObject);
if (returnType == typeof(System.UInt64)) if (returnType == typeof(System.UInt64))
return (IntPtr)new UIntPtr((ulong)(System.UInt64)(object)returnObject); return (IntPtr)new UIntPtr((ulong)(System.UInt64)(object)returnObject);
return ManagedHandle.ToIntPtr(returnObject, GCHandleType.Weak); return ManagedHandle.ToIntPtr(returnObject/*, GCHandleType.Weak*/);
} }
#if !USE_AOT #if !USE_AOT

View File

@@ -1,5 +1,10 @@
// Copyright (c) Wojciech Figat. All rights reserved. // Copyright (c) Wojciech Figat. All rights reserved.
#define USE_CONCURRENT_DICT
#define USE_GCHANDLE
//#define TRACK_HANDLES
#if USE_NETCORE #if USE_NETCORE
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@@ -8,6 +13,7 @@ using System.Runtime.InteropServices;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Threading; using System.Threading;
using FlaxEngine.Assertions; using FlaxEngine.Assertions;
using System.Collections.Concurrent;
#pragma warning disable 1591 #pragma warning disable 1591
@@ -346,6 +352,14 @@ namespace FlaxEngine.Interop
return; return;
handle.Free(); handle.Free();
} }
[System.Diagnostics.DebuggerStepThrough]
public static void Free(ManagedHandle handle)
{
if (handle == EmptyStringHandle)
return;
handle.Free();
}
} }
/// <summary> /// <summary>
@@ -356,18 +370,106 @@ namespace FlaxEngine.Interop
#endif #endif
public struct ManagedHandle public struct ManagedHandle
{ {
#if USE_GCHANDLE
private GCHandle handle;
#if TRACK_HANDLES
private static HashSet<IntPtr> _weakHandles = new HashSet<nint>();
private static ConcurrentDictionary<IntPtr, string> _handles = new();
private static ConcurrentDictionary<IntPtr, string> _handles2 = new();
private static long _allocations = 0;
private static long _deallocations = 0;
public static long Allocations => Interlocked.Read(ref _allocations);
public static long Deallocations => Interlocked.Read(ref _deallocations);
#else
public static long Allocations => 0;
public static long Deallocations => 0;
#endif
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private ManagedHandle(IntPtr handle) => this.handle = GCHandle.FromIntPtr(handle);
//[MethodImpl(MethodImplOptions.AggressiveInlining)]
private ManagedHandle(object value, GCHandleType type) //=> handle = GCHandle.Alloc(value, type);
{
#if TRACK_HANDLES
/*if (type == GCHandleType.Weak || type == GCHandleType.WeakTrackResurrection)
{
type = GCHandleType.Normal;
}*/
#endif
handle = GCHandle.Alloc(value, type);
#if TRACK_HANDLES
/*if (type == GCHandleType.Weak || type == GCHandleType.WeakTrackResurrection)
_weakHandles.Add((IntPtr)handle);
else if (_handles.Count < 14000)
_handles.TryAdd((IntPtr)handle, value?.GetType().FullName ?? "");
else
{
if (_handles2.Count > 12)
type = type;
if (value?.GetType() == typeof(string))
type = type;
_handles2.TryAdd((IntPtr)handle, value?.GetType().FullName ?? "");
}*/
Interlocked.Increment(ref _allocations);
#endif
}
#else
private IntPtr handle; private IntPtr handle;
private ManagedHandle(IntPtr handle) => this.handle = handle; private ManagedHandle(IntPtr handle) => this.handle = handle;
private ManagedHandle(object value, GCHandleType type) => handle = ManagedHandlePool.AllocateHandle(value, type); private ManagedHandle(object value, GCHandleType type) => handle = ManagedHandlePool.AllocateHandle(value, type);
#endif
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ManagedHandle Alloc(object value) => new ManagedHandle(value, GCHandleType.Normal); public static ManagedHandle Alloc(object value) => new ManagedHandle(value, GCHandleType.Normal);
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ManagedHandle Alloc(object value, GCHandleType type) => new ManagedHandle(value, type); public static ManagedHandle Alloc(object value, GCHandleType type) => new ManagedHandle(value, type);
#if USE_GCHANDLE
public void Free()// => handle.Free();
{
if ((IntPtr)handle == IntPtr.Zero)
return;
#if TRACK_HANDLES
/*if (_weakHandles.Remove((IntPtr)handle))
{
if (!handle.IsAllocated)
handle = handle;
handle = handle;
}
else if (_handles.Remove((IntPtr)handle, out _))
{
handle = handle;
}
else if (_handles2.Remove((IntPtr)handle, out _))
{
handle = handle;
}*/
Interlocked.Increment(ref _deallocations);
#endif
handle.Free();
}
public object Target
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => handle.Target;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set => handle.Target = value;
}
public bool IsAllocated
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => handle.IsAllocated;
}
#else
public void Free() public void Free()
{ {
if (handle == IntPtr.Zero) if (handle == IntPtr.Zero)
@@ -383,6 +485,7 @@ namespace FlaxEngine.Interop
} }
public bool IsAllocated => handle != IntPtr.Zero; public bool IsAllocated => handle != IntPtr.Zero;
#endif
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static explicit operator ManagedHandle(IntPtr value) => FromIntPtr(value); public static explicit operator ManagedHandle(IntPtr value) => FromIntPtr(value);
@@ -393,6 +496,16 @@ namespace FlaxEngine.Interop
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static explicit operator IntPtr(ManagedHandle value) => ToIntPtr(value); public static explicit operator IntPtr(ManagedHandle value) => ToIntPtr(value);
#if USE_GCHANDLE
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static IntPtr ToIntPtr(object value) => (IntPtr)Alloc(value, GCHandleType.Normal).handle;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static IntPtr ToIntPtr(object value, GCHandleType type) => (IntPtr)Alloc(value, type).handle;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static IntPtr ToIntPtr(ManagedHandle value) => (IntPtr)value.handle;
#else
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static IntPtr ToIntPtr(object value) => ManagedHandlePool.AllocateHandle(value, GCHandleType.Normal); public static IntPtr ToIntPtr(object value) => ManagedHandlePool.AllocateHandle(value, GCHandleType.Normal);
@@ -401,17 +514,69 @@ namespace FlaxEngine.Interop
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static IntPtr ToIntPtr(ManagedHandle value) => value.handle; public static IntPtr ToIntPtr(ManagedHandle value) => value.handle;
#endif
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetHashCode() => handle.GetHashCode(); public override int GetHashCode() => handle.GetHashCode();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool Equals(object obj) => obj is ManagedHandle other && handle == other.handle; public override bool Equals(object obj) => obj is ManagedHandle other && handle == other.handle;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(ManagedHandle other) => handle == other.handle; public bool Equals(ManagedHandle other) => handle == other.handle;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator ==(ManagedHandle a, ManagedHandle b) => a.handle == b.handle; public static bool operator ==(ManagedHandle a, ManagedHandle b) => a.handle == b.handle;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(ManagedHandle a, ManagedHandle b) => a.handle != b.handle; public static bool operator !=(ManagedHandle a, ManagedHandle b) => a.handle != b.handle;
#if USE_GCHANDLE
/// <summary>
/// A pool of shared managed handles used with short-lived temporary GCHandle allocations during interop.
/// </summary>
internal static class ManagedHandlePool
{
[ThreadStatic]
private static List<(bool InUse, ManagedHandle Handle)> _pool;
internal static void TryCollectWeakHandles(bool force = false)
{
}
internal static ManagedHandle Rent()
{
foreach (ref var tuple in CollectionsMarshal.AsSpan(_pool))
{
if (tuple.InUse)
continue;
tuple.InUse = true;
return tuple.Handle;
}
var newTuple = (InUse: true, Array: new ManagedHandle());
_pool.Add(newTuple);
return newTuple.Array;
}
internal static void Return(ManagedHandle handle)
{
foreach (ref var tuple in CollectionsMarshal.AsSpan(_pool))
{
if (tuple.Handle != handle)
continue;
tuple.InUse = false;
handle.Target = null;
return;
}
throw new Exception("Tried to return non-pooled ManagedHandle");
}
}
#else
internal static class ManagedHandlePool internal static class ManagedHandlePool
{ {
private const int WeakPoolCollectionSizeThreshold = 10000000; private const int WeakPoolCollectionSizeThreshold = 10000000;
@@ -425,17 +590,31 @@ namespace FlaxEngine.Interop
// Dictionaries for storing the valid handles. // Dictionaries for storing the valid handles.
// Note: Using locks seems to be generally the fastest when adding or fetching from the dictionary. // Note: Using locks seems to be generally the fastest when adding or fetching from the dictionary.
// Concurrent dictionaries could also be considered, but they perform much slower when adding to the dictionary. // Concurrent dictionaries could also be considered, but they perform much slower when adding to the dictionary.
#if USE_CONCURRENT_DICT
private static ConcurrentDictionary<IntPtr, object> persistentPool = new();
private static ConcurrentDictionary<IntPtr, GCHandle> pinnedPool = new();
#else
private static Dictionary<IntPtr, object> persistentPool = new(); private static Dictionary<IntPtr, object> persistentPool = new();
private static Dictionary<IntPtr, GCHandle> pinnedPool = new(); private static Dictionary<IntPtr, GCHandle> pinnedPool = new();
private static Lock persistentPoolLock = new();
private static Lock pinnedPoolLock = new();
#endif
// TODO: Performance of pinned handles are poor at the moment due to GCHandle wrapping. // TODO: Performance of pinned handles are poor at the moment due to GCHandle wrapping.
// TODO: .NET8: Experiment with pinned arrays for faster pinning: https://github.com/dotnet/runtime/pull/89293 // TODO: .NET8: Experiment with pinned arrays for faster pinning: https://github.com/dotnet/runtime/pull/89293
// Manage double-buffered pool for weak handles in order to avoid collecting in-flight handles. // Manage double-buffered pool for weak handles in order to avoid collecting in-flight handles.
// Periodically when the pools are being accessed and conditions are met, the other pool is cleared and swapped. // Periodically when the pools are being accessed and conditions are met, the other pool is cleared and swapped.
private static Dictionary<IntPtr, object> weakPool = new(); private static int weakPoolSize = 1;
private static Dictionary<IntPtr, object> weakPoolOther = new(); private static int weakPoolOtherSize = 1;
private static object weakPoolLock = new object(); #if USE_CONCURRENT_DICT
private static ConcurrentDictionary<IntPtr, object> weakPool = new(-1, weakPoolSize);
private static ConcurrentDictionary<IntPtr, object> weakPoolOther = new(-1, weakPoolOtherSize);
#else
private static Dictionary<IntPtr, object> weakPool = new(weakPoolSize);
private static Dictionary<IntPtr, object> weakPoolOther = new(weakPoolOtherSize);
#endif
private static Lock weakPoolLock = new();
private static ulong nextWeakPoolCollection; private static ulong nextWeakPoolCollection;
private static int nextWeakPoolGCCollection; private static int nextWeakPoolGCCollection;
private static long lastWeakPoolCollectionTime; private static long lastWeakPoolCollectionTime;
@@ -466,6 +645,19 @@ namespace FlaxEngine.Interop
// Swap the pools and release the oldest pool for GC // Swap the pools and release the oldest pool for GC
(weakPool, weakPoolOther) = (weakPoolOther, weakPool); (weakPool, weakPoolOther) = (weakPoolOther, weakPool);
(weakPoolSize, weakPoolOtherSize) = (weakPoolOtherSize, weakPoolSize);
#if USE_CONCURRENT_DICT
if (weakPool.Count > weakPoolSize)
{
var newMax = Math.Max(Math.Max(weakPoolSize, weakPoolOtherSize), weakPool.Count);
//FlaxEditor.Editor.Log($"growth from {weakPoolSize} to {weakPool.Count}, max {newMax}");
weakPoolSize = newMax;
weakPool = new(-1, weakPoolSize);
}
//else if (weakPool.Count > 0)
// weakPool.Clear();
//else
#endif
weakPool.Clear(); weakPool.Clear();
} }
@@ -485,14 +677,43 @@ namespace FlaxEngine.Interop
internal static IntPtr AllocateHandle(object value, GCHandleType type) internal static IntPtr AllocateHandle(object value, GCHandleType type)
{ {
IntPtr handle = NewHandle(type); IntPtr handle = NewHandle(type);
#if USE_CONCURRENT_DICT
switch (type) switch (type)
{ {
case GCHandleType.Normal: case GCHandleType.Normal:
lock (persistentPool) //lock (persistentPoolLock)
persistentPool.Add(handle, value); persistentPool.TryAdd(handle, value);
//if (value?.GetType().Name.Contains("RenderContext") ?? false)
// value = value;
break; break;
case GCHandleType.Pinned: case GCHandleType.Pinned:
lock (pinnedPool) //lock (pinnedPoolLock)
pinnedPool.TryAdd(handle, GCHandle.Alloc(value, GCHandleType.Pinned));
break;
case GCHandleType.Weak:
case GCHandleType.WeakTrackResurrection:
lock (weakPoolLock)
{
TryCollectWeakHandles();
//weakPool.TryAdd(handle, value);
weakPool[handle] = value;
//if (value?.GetType().Name.Contains("RenderContext") ?? false)
// value = value;
}
break;
}
#else
switch (type)
{
case GCHandleType.Normal:
lock (persistentPoolLock)
persistentPool.Add(handle, value);
//if (value?.GetType().Name.Contains("RenderContext") ?? false)
// value = value;
break;
case GCHandleType.Pinned:
lock (pinnedPoolLock)
pinnedPool.Add(handle, GCHandle.Alloc(value, GCHandleType.Pinned)); pinnedPool.Add(handle, GCHandle.Alloc(value, GCHandleType.Pinned));
break; break;
case GCHandleType.Weak: case GCHandleType.Weak:
@@ -501,25 +722,36 @@ namespace FlaxEngine.Interop
{ {
TryCollectWeakHandles(); TryCollectWeakHandles();
weakPool.Add(handle, value); weakPool.Add(handle, value);
//weakPool[handle] = value;
//if (value?.GetType().Name.Contains("RenderContext") ?? false)
// value = value;
} }
break; break;
} }
#endif
return handle; return handle;
} }
internal static object GetObject(IntPtr handle) internal static object GetObject(IntPtr handle)
{ {
switch (GetHandleType(handle)) GCHandleType type = GetHandleType(handle);
switch (type)
{ {
case GCHandleType.Normal: case GCHandleType.Normal:
lock (persistentPool) #if !USE_CONCURRENT_DICT
lock (persistentPoolLock)
#endif
{ {
if (persistentPool.TryGetValue(handle, out object value)) if (persistentPool.TryGetValue(handle, out object value))
return value; return value;
} }
break; break;
case GCHandleType.Pinned: case GCHandleType.Pinned:
lock (pinnedPool) #if !USE_CONCURRENT_DICT
lock (pinnedPoolLock)
#endif
{ {
if (pinnedPool.TryGetValue(handle, out GCHandle gcHandle)) if (pinnedPool.TryGetValue(handle, out GCHandle gcHandle))
return gcHandle.Target; return gcHandle.Target;
@@ -537,15 +769,98 @@ namespace FlaxEngine.Interop
} }
break; break;
} }
throw new NativeInteropException("Invalid ManagedHandle"); throw new NativeInteropException($"Invalid ManagedHandle of type '{type}'");
} }
internal static void SetObject(IntPtr handle, object value) internal static void SetObject(IntPtr handle, object value)
{ {
switch (GetHandleType(handle)) GCHandleType type = GetHandleType(handle);
#if USE_CONCURRENT_DICT
switch (type)
{ {
case GCHandleType.Normal: case GCHandleType.Normal:
lock (persistentPool) //lock (persistentPoolLock)
{
//ref object obj = ref CollectionsMarshal.GetValueRefOrNullRef(persistentPool, handle);
if (persistentPool.TryGetValue(handle, out var oldValue))
{
if (persistentPool.TryUpdate(handle, value, oldValue))
//if (!Unsafe.IsNullRef(ref obj))
{
//obj = value;
return;
}
}
}
break;
case GCHandleType.Pinned:
//lock (pinnedPoolLock)
{
/*ref GCHandle gcHandle = ref CollectionsMarshal.GetValueRefOrNullRef(pinnedPool, handle);
if (!Unsafe.IsNullRef(ref gcHandle))
{
gcHandle.Target = value;
return;
}*/
if (pinnedPool.TryGetValue(handle, out var gcHandle))
{
gcHandle.Target = value;
//if (pinnedPool.TryUpdate(handle, value, oldValue))
//if (!Unsafe.IsNullRef(ref obj))
//{
//obj = value;
return;
}
//}
}
break;
case GCHandleType.Weak:
case GCHandleType.WeakTrackResurrection:
lock (weakPoolLock)
{
TryCollectWeakHandles();
if (weakPool.TryGetValue(handle, out var oldValue))
{
if (weakPool.TryUpdate(handle, value, oldValue))
//if (!Unsafe.IsNullRef(ref obj))
{
//obj = value;
return;
}
}
if (weakPoolOther.TryGetValue(handle, out oldValue))
{
if (weakPoolOther.TryUpdate(handle, value, oldValue))
//if (!Unsafe.IsNullRef(ref obj))
{
//obj = value;
return;
}
}
/*{
ref object obj = ref CollectionsMarshal.GetValueRefOrNullRef(weakPool, handle);
if (!Unsafe.IsNullRef(ref obj))
{
obj = value;
return;
}
}
{
ref object obj = ref CollectionsMarshal.GetValueRefOrNullRef(weakPoolOther, handle);
if (!Unsafe.IsNullRef(ref obj))
{
obj = value;
return;
}
}*/
}
break;
}
#else
switch (type)
{
case GCHandleType.Normal:
lock (persistentPoolLock)
{ {
ref object obj = ref CollectionsMarshal.GetValueRefOrNullRef(persistentPool, handle); ref object obj = ref CollectionsMarshal.GetValueRefOrNullRef(persistentPool, handle);
if (!Unsafe.IsNullRef(ref obj)) if (!Unsafe.IsNullRef(ref obj))
@@ -556,7 +871,7 @@ namespace FlaxEngine.Interop
} }
break; break;
case GCHandleType.Pinned: case GCHandleType.Pinned:
lock (pinnedPool) lock (pinnedPoolLock)
{ {
ref GCHandle gcHandle = ref CollectionsMarshal.GetValueRefOrNullRef(pinnedPool, handle); ref GCHandle gcHandle = ref CollectionsMarshal.GetValueRefOrNullRef(pinnedPool, handle);
if (!Unsafe.IsNullRef(ref gcHandle)) if (!Unsafe.IsNullRef(ref gcHandle))
@@ -590,22 +905,32 @@ namespace FlaxEngine.Interop
} }
break; break;
} }
throw new NativeInteropException("Invalid ManagedHandle"); #endif
throw new NativeInteropException($"Invalid ManagedHandle of type '{type}'");
} }
internal static void FreeHandle(IntPtr handle) internal static void FreeHandle(IntPtr handle)
{ {
switch (GetHandleType(handle)) GCHandleType type = GetHandleType(handle);
switch (type)
{ {
case GCHandleType.Normal: case GCHandleType.Normal:
lock (persistentPool) #if !USE_CONCURRENT_DICT
lock (persistentPoolLock)
#endif
{ {
if (persistentPool.Remove(handle)) if (persistentPool.Remove(handle, out _))
{
//if (value?.GetType().Name.Contains("RenderContext") ?? false)
// value = value;
return; return;
} }
}
break; break;
case GCHandleType.Pinned: case GCHandleType.Pinned:
lock (pinnedPool) #if !USE_CONCURRENT_DICT
lock (pinnedPoolLock)
#endif
{ {
if (pinnedPool.Remove(handle, out GCHandle gcHandle)) if (pinnedPool.Remove(handle, out GCHandle gcHandle))
{ {
@@ -617,12 +942,19 @@ namespace FlaxEngine.Interop
case GCHandleType.Weak: case GCHandleType.Weak:
case GCHandleType.WeakTrackResurrection: case GCHandleType.WeakTrackResurrection:
lock (weakPoolLock) lock (weakPoolLock)
{
TryCollectWeakHandles(); TryCollectWeakHandles();
if (weakPool.Remove(handle, out _))
return;
else if (weakPoolOther.Remove(handle, out _))
return;
return; return;
} }
throw new NativeInteropException("Invalid ManagedHandle"); }
throw new NativeInteropException($"Invalid ManagedHandle of type '{type}'");
} }
} }
#endif
} }
} }

View File

@@ -59,8 +59,9 @@ namespace FlaxEngine.Interop
#endif #endif
public struct ManagedToNativeState public struct ManagedToNativeState
{ {
ManagedArray managedArray; ManagedArray _pooledManagedArray;
IntPtr handle; ManagedArray _managedArray;
ManagedHandle handle;
public void FromManaged(object managed) public void FromManaged(object managed)
{ {
@@ -73,29 +74,30 @@ namespace FlaxEngine.Interop
if (NativeInterop.ArrayFactory.GetMarshalledType(elementType) == elementType) if (NativeInterop.ArrayFactory.GetMarshalledType(elementType) == elementType)
{ {
// Use pooled managed array wrapper to be passed around as handle to it // Use pooled managed array wrapper to be passed around as handle to it
(ManagedHandle tmp, managedArray) = ManagedArray.WrapPooledArray(arr); (handle, _pooledManagedArray) = ManagedArray.WrapPooledArray(arr);
handle = ManagedHandle.ToIntPtr(tmp);
} }
else else
{ {
// Convert array contents to be properly accessed by the native code (as GCHandles array) // Convert array contents to be properly accessed by the native code (as GCHandles array)
managedArray = NativeInterop.ManagedArrayToGCHandleWrappedArray(arr); _managedArray = NativeInterop.ManagedArrayToGCHandleWrappedArray(arr);
handle = ManagedHandle.ToIntPtr(ManagedHandle.Alloc(managedArray)); handle = ManagedHandle.Alloc(_pooledManagedArray);
managedArray = null; // It's not pooled
} }
} }
else else
handle = ManagedHandle.ToIntPtr(managed, GCHandleType.Weak); handle = ManagedHandle.Alloc(managed/*, GCHandleType.Weak*/);
} }
public IntPtr ToUnmanaged() public IntPtr ToUnmanaged() => ManagedHandle.ToIntPtr(handle);
{
return handle;
}
public void Free() public void Free()
{ {
managedArray?.FreePooled(); if (_pooledManagedArray != null)
_pooledManagedArray.FreePooled();
else
{
handle.Free();
_managedArray?.Free();
}
} }
} }
@@ -109,6 +111,7 @@ namespace FlaxEngine.Interop
public static void Free(IntPtr unmanaged) public static void Free(IntPtr unmanaged)
{ {
ManagedHandle.FromIntPtr(unmanaged).Free();
} }
} }
@@ -409,7 +412,7 @@ namespace FlaxEngine.Interop
public static void Free(IntPtr unmanaged) public static void Free(IntPtr unmanaged)
{ {
//DictionaryMarshaller<T, U>.Free(unmanaged); // No need to free weak handles DictionaryMarshaller<T, U>.Free(unmanaged); // No need to free weak handles
} }
} }
@@ -678,7 +681,7 @@ namespace FlaxEngine.Interop
public static class NativeToManaged public static class NativeToManaged
{ {
public static string ConvertToManaged(IntPtr unmanaged) => ManagedString.ToManaged(unmanaged); public static string ConvertToManaged(IntPtr unmanaged) => ManagedString.ToManaged(unmanaged);
public static unsafe IntPtr ConvertToUnmanaged(string managed) => managed == null ? IntPtr.Zero : ManagedHandle.ToIntPtr(managed, GCHandleType.Weak); public static unsafe IntPtr ConvertToUnmanaged(string managed) => managed == null ? IntPtr.Zero : ManagedHandle.ToIntPtr(managed/*, GCHandleType.Weak*/);
public static void Free(IntPtr unmanaged) => ManagedString.Free(unmanaged); public static void Free(IntPtr unmanaged) => ManagedString.Free(unmanaged);
} }
@@ -688,11 +691,11 @@ namespace FlaxEngine.Interop
public static class ManagedToNative public static class ManagedToNative
{ {
public static string ConvertToManaged(IntPtr unmanaged) => ManagedString.ToManaged(unmanaged); public static string ConvertToManaged(IntPtr unmanaged) => ManagedString.ToManaged(unmanaged);
public static unsafe IntPtr ConvertToUnmanaged(string managed) => managed == null ? IntPtr.Zero : ManagedHandle.ToIntPtr(managed, GCHandleType.Weak); public static unsafe IntPtr ConvertToUnmanaged(string managed) => managed == null ? IntPtr.Zero : ManagedHandle.ToIntPtr(managed/*, GCHandleType.Weak*/);
public static void Free(IntPtr unmanaged) public static void Free(IntPtr unmanaged)
{ {
//ManagedString.Free(unmanaged); // No need to free weak handles ManagedString.Free(unmanaged); // No need to free weak handles
} }
} }

View File

@@ -9,6 +9,8 @@ using System.Reflection;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Runtime.Loader; using System.Runtime.Loader;
using System.Threading;
using FlaxEngine.Assertions;
using FlaxEngine.Utilities; using FlaxEngine.Utilities;
#pragma warning disable 1591 #pragma warning disable 1591
@@ -543,6 +545,18 @@ namespace FlaxEngine.Interop
} }
} }
[UnmanagedCallersOnly]
internal static void FreeArray(ManagedHandle handle)
{
if (!handle.IsAllocated)
return;
ManagedArray managedArray = Unsafe.As<ManagedArray>(handle.Target);
if (managedArray.ElementType.IsValueType)
managedArray.Free();
else
managedArray.FreePooled();
}
[UnmanagedCallersOnly] [UnmanagedCallersOnly]
internal static ManagedHandle GetArrayTypeFromElementType(ManagedHandle elementTypeHandle) internal static ManagedHandle GetArrayTypeFromElementType(ManagedHandle elementTypeHandle)
{ {
@@ -602,13 +616,19 @@ namespace FlaxEngine.Interop
[UnmanagedCallersOnly] [UnmanagedCallersOnly]
internal static IntPtr NewStringUTF16(char* text, int length) internal static IntPtr NewStringUTF16(char* text, int length)
{ {
return ManagedString.ToNativeWeak(new string(new ReadOnlySpan<char>(text, length))); return ManagedString.ToNative/*Weak*/(new string(new ReadOnlySpan<char>(text, length)));
} }
[UnmanagedCallersOnly] [UnmanagedCallersOnly]
internal static IntPtr NewStringUTF8(sbyte* text, int length) internal static IntPtr NewStringUTF8(sbyte* text, int length)
{ {
return ManagedString.ToNativeWeak(new string(text, 0, length, System.Text.Encoding.UTF8)); return ManagedString.ToNative/*Weak*/(new string(text, 0, length, System.Text.Encoding.UTF8));
}
[UnmanagedCallersOnly]
internal static void FreeString(ManagedHandle handle)
{
ManagedString.Free(handle);
} }
[UnmanagedCallersOnly] [UnmanagedCallersOnly]
@@ -654,27 +674,30 @@ namespace FlaxEngine.Interop
} }
/// <summary> /// <summary>
/// Creates a managed copy of the value, and stores it in a boxed reference. /// Creates a managed converted copy of the unmanaged value, and boxes it in a managed handle.
/// </summary> /// </summary>
/// <param name="typeHandle">A handle to class type.</param>
/// <param name="valuePtr">A pointer to unmanaged value.</param>
[UnmanagedCallersOnly] [UnmanagedCallersOnly]
internal static ManagedHandle BoxValue(ManagedHandle typeHandle, IntPtr valuePtr) internal static ManagedHandle BoxValue(ManagedHandle typeHandle, IntPtr valuePtr)
{ {
Type type = Unsafe.As<TypeHolder>(typeHandle.Target); Type type = Unsafe.As<TypeHolder>(typeHandle.Target);
object value = MarshalToManaged(valuePtr, type); object value = MarshalToManaged(valuePtr, type);
return ManagedHandle.Alloc(value, GCHandleType.Weak); return ManagedHandle.Alloc(value/*, GCHandleType.Weak*/);
} }
/// <summary> /// <summary>
/// Returns the address of the boxed value type. /// Writes the unboxed converted value to given address.
/// </summary> /// </summary>
/// <param name="objectHandle">A handle to boxed object.</param>
/// <param name="destinationPtr">The destination address for unboxed value.</param>
[UnmanagedCallersOnly] [UnmanagedCallersOnly]
internal static IntPtr UnboxValue(ManagedHandle handle) internal static void UnboxValue(ManagedHandle objectHandle, IntPtr destinationPtr)
{ {
object value = handle.Target; object value = objectHandle.Target;
Assert.IsNotNull(value, "Boxed value type can't be null.");
Type type = value.GetType(); Type type = value.GetType();
if (!type.IsValueType) MarshalToNative(objectHandle.Target, destinationPtr, type);
return ManagedHandle.ToIntPtr(handle);
return ValueTypeUnboxer.GetPointer(value, type);
} }
[UnmanagedCallersOnly] [UnmanagedCallersOnly]
@@ -714,7 +737,7 @@ namespace FlaxEngine.Interop
internal static IntPtr InvokeMethod(ManagedHandle instanceHandle, ManagedHandle methodHandle, IntPtr paramPtr, IntPtr exceptionPtr) internal static IntPtr InvokeMethod(ManagedHandle instanceHandle, ManagedHandle methodHandle, IntPtr paramPtr, IntPtr exceptionPtr)
{ {
MethodHolder methodHolder = Unsafe.As<MethodHolder>(methodHandle.Target); MethodHolder methodHolder = Unsafe.As<MethodHolder>(methodHandle.Target);
#if !USE_AOT #if false//#if !USE_AOT
if (methodHolder.TryGetDelegate(out var methodDelegate, out var methodDelegateContext)) if (methodHolder.TryGetDelegate(out var methodDelegate, out var methodDelegateContext))
{ {
// Fast path, invoke the method with minimal allocations // Fast path, invoke the method with minimal allocations
@@ -737,7 +760,7 @@ namespace FlaxEngine.Interop
// Slow path, method parameters needs to be stored in heap // Slow path, method parameters needs to be stored in heap
object returnObject; object returnObject;
int numParams = methodHolder.parameterTypes.Length; int numParams = methodHolder.parameterTypes.Length;
object[] methodParameters = new object[numParams]; object[] methodParameters = new object[numParams];//ObjectArrayPool.Rent(numParams);//new object[numParams];
for (int i = 0; i < numParams; i++) for (int i = 0; i < numParams; i++)
{ {
@@ -748,6 +771,17 @@ namespace FlaxEngine.Interop
try try
{ {
returnObject = methodHolder.method.Invoke(instanceHandle.IsAllocated ? instanceHandle.Target : null, methodParameters); returnObject = methodHolder.method.Invoke(instanceHandle.IsAllocated ? instanceHandle.Target : null, methodParameters);
// Marshal reference parameters back to original unmanaged references
for (int i = 0; i < numParams; i++)
{
Type parameterType = methodHolder.parameterTypes[i];
if (parameterType.IsByRef)
{
IntPtr nativePtr = Unsafe.Read<IntPtr>((IntPtr.Add(paramPtr, sizeof(IntPtr) * i)).ToPointer());
MarshalToNative(methodParameters[i], nativePtr, parameterType.GetElementType());
}
}
} }
catch (Exception exception) catch (Exception exception)
{ {
@@ -762,19 +796,11 @@ namespace FlaxEngine.Interop
throw realException; throw realException;
return IntPtr.Zero; return IntPtr.Zero;
} }
finally
// Marshal reference parameters back to original unmanaged references
for (int i = 0; i < numParams; i++)
{ {
Type parameterType = methodHolder.parameterTypes[i]; //ObjectArrayPool.Return(methodParameters);
if (parameterType.IsByRef)
{
IntPtr nativePtr = Unsafe.Read<IntPtr>((IntPtr.Add(paramPtr, sizeof(IntPtr) * i)).ToPointer());
MarshalToNative(methodParameters[i], nativePtr, parameterType.GetElementType());
}
} }
// Return value
return Invoker.MarshalReturnValueGeneric(methodHolder.returnType, returnObject); return Invoker.MarshalReturnValueGeneric(methodHolder.returnType, returnObject);
} }
} }
@@ -908,7 +934,7 @@ namespace FlaxEngine.Interop
if (File.Exists(pdbPath)) if (File.Exists(pdbPath))
{ {
// Load including debug symbols // Load including debug symbols
using FileStream pdbStream = new FileStream(Path.ChangeExtension(assemblyPath, "pdb"), FileMode.Open); using FileStream pdbStream = new FileStream(Path.ChangeExtension(assemblyPath, "pdb"), FileMode.Open, FileAccess.Read);
assembly = scriptingAssemblyLoadContext.LoadFromStream(stream, pdbStream); assembly = scriptingAssemblyLoadContext.LoadFromStream(stream, pdbStream);
} }
else else
@@ -998,6 +1024,8 @@ namespace FlaxEngine.Interop
Debug.Logger.LogHandler.LogWrite(LogType.Warning, "Scripting AssemblyLoadContext was not unloaded."); Debug.Logger.LogHandler.LogWrite(LogType.Warning, "Scripting AssemblyLoadContext was not unloaded.");
weakRef.Free(); weakRef.Free();
Assert.IsFalse(AssemblyLoadContext.All.Any(x => x.Name == "Flax"));
static bool IsHandleAlive(GCHandle weakRef) static bool IsHandleAlive(GCHandle weakRef)
{ {
// Checking the target in scope somehow holds a reference to it...? // Checking the target in scope somehow holds a reference to it...?
@@ -1026,6 +1054,7 @@ namespace FlaxEngine.Interop
// Clear all caches which might hold references to assemblies in collectible ALC // Clear all caches which might hold references to assemblies in collectible ALC
cachedDelegatesCollectible.Clear(); cachedDelegatesCollectible.Clear();
cachedDelegatesCollectible = new();
foreach (var pair in managedTypesCollectible) foreach (var pair in managedTypesCollectible)
pair.Value.handle.Free(); pair.Value.handle.Free();
managedTypesCollectible.Clear(); managedTypesCollectible.Clear();
@@ -1115,6 +1144,9 @@ namespace FlaxEngine.Interop
} }
} }
GC.Collect();
GC.WaitForPendingFinalizers();
// Unload the ALC // Unload the ALC
scriptingAssemblyLoadContext.Unload(); scriptingAssemblyLoadContext.Unload();
scriptingAssemblyLoadContext.Resolving -= OnScriptingAssemblyLoadContextResolving; scriptingAssemblyLoadContext.Resolving -= OnScriptingAssemblyLoadContextResolving;

View File

@@ -18,6 +18,7 @@ using System.Collections.Concurrent;
using System.IO; using System.IO;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Buffers;
namespace FlaxEngine.Interop namespace FlaxEngine.Interop
{ {
@@ -1244,7 +1245,7 @@ namespace FlaxEngine.Interop
internal static void ToNativeString(ref string managedValue, IntPtr nativePtr) internal static void ToNativeString(ref string managedValue, IntPtr nativePtr)
{ {
Unsafe.Write<IntPtr>(nativePtr.ToPointer(), ManagedString.ToNativeWeak(managedValue)); Unsafe.Write<IntPtr>(nativePtr.ToPointer(), ManagedString.ToNative/*Weak*/(managedValue));
} }
internal static void ToNativeType(ref Type managedValue, IntPtr nativePtr) internal static void ToNativeType(ref Type managedValue, IntPtr nativePtr)
@@ -1291,14 +1292,14 @@ namespace FlaxEngine.Interop
} }
else else
managedArray = ManagedArrayToGCHandleWrappedArray(arr); managedArray = ManagedArrayToGCHandleWrappedArray(arr);
managedPtr = ManagedHandle.ToIntPtr(managedArray, GCHandleType.Weak); managedPtr = ManagedHandle.ToIntPtr(managedArray/*, GCHandleType.Weak*/);
} }
Unsafe.Write<IntPtr>(nativePtr.ToPointer(), managedPtr); Unsafe.Write<IntPtr>(nativePtr.ToPointer(), managedPtr);
} }
internal static void ToNative(ref T managedValue, IntPtr nativePtr) internal static void ToNative(ref T managedValue, IntPtr nativePtr)
{ {
Unsafe.Write<IntPtr>(nativePtr.ToPointer(), managedValue != null ? ManagedHandle.ToIntPtr(managedValue, GCHandleType.Weak) : IntPtr.Zero); Unsafe.Write<IntPtr>(nativePtr.ToPointer(), managedValue != null ? ManagedHandle.ToIntPtr(managedValue/*, GCHandleType.Weak*/) : IntPtr.Zero);
} }
} }
@@ -1529,11 +1530,12 @@ namespace FlaxEngine.Interop
private static GCHandle[] pinnedBoxedValues = new GCHandle[256]; private static GCHandle[] pinnedBoxedValues = new GCHandle[256];
private static uint pinnedBoxedValuesPointer = 0; private static uint pinnedBoxedValuesPointer = 0;
private static (IntPtr ptr, int size)[] pinnedAllocations = new (IntPtr ptr, int size)[256]; private static (IntPtr ptr, int size)[] pinnedAllocations = new (IntPtr ptr, int size)[256];
private static Action[] pinnedNativeFreeDelegates = new Action[256];
private static uint pinnedAllocationsPointer = 0; private static uint pinnedAllocationsPointer = 0;
private delegate IntPtr UnboxerDelegate(object value, object converter); private delegate IntPtr UnboxerDelegate(object value, object toNativeConverter, object nativeFree);
private static ConcurrentDictionary<Type, (UnboxerDelegate deleg, object toNativeDeleg)> unboxers = new(1, 3); private static ConcurrentDictionary<Type, (UnboxerDelegate deleg, object/*Delegate*/ toNativeConverter, object nativeFree)> unboxers = new(1, 3);
private static MethodInfo unboxerMethod = typeof(ValueTypeUnboxer).GetMethod(nameof(ValueTypeUnboxer.UnboxPointer), BindingFlags.Static | BindingFlags.NonPublic); private static MethodInfo unboxerMethod = typeof(ValueTypeUnboxer).GetMethod(nameof(ValueTypeUnboxer.UnboxPointer), BindingFlags.Static | BindingFlags.NonPublic);
private static MethodInfo unboxerToNativeMethod = typeof(ValueTypeUnboxer).GetMethod(nameof(ValueTypeUnboxer.UnboxPointerWithConverter), BindingFlags.Static | BindingFlags.NonPublic); private static MethodInfo unboxerToNativeMethod = typeof(ValueTypeUnboxer).GetMethod(nameof(ValueTypeUnboxer.UnboxPointerWithConverter), BindingFlags.Static | BindingFlags.NonPublic);
@@ -1546,8 +1548,12 @@ namespace FlaxEngine.Interop
var toNativeMethod = attr?.NativeType.GetMethod("ToNative", BindingFlags.Static | BindingFlags.NonPublic); var toNativeMethod = attr?.NativeType.GetMethod("ToNative", BindingFlags.Static | BindingFlags.NonPublic);
if (toNativeMethod != null) if (toNativeMethod != null)
{ {
tuple.deleg = unboxerToNativeMethod.MakeGenericMethod(type, toNativeMethod.ReturnType).CreateDelegate<UnboxerDelegate>(); Type internalType = toNativeMethod.ReturnType;
tuple.toNativeDeleg = toNativeMethod.CreateDelegate(typeof(ToNativeDelegate<,>).MakeGenericType(type, toNativeMethod.ReturnType)); tuple.deleg = unboxerToNativeMethod.MakeGenericMethod(type, internalType).CreateDelegate<UnboxerDelegate>();
tuple.toNativeConverter = toNativeMethod.CreateDelegate(typeof(ToNativeDelegate<,>).MakeGenericType(type, internalType));
MethodInfo freeNativeMethod = attr.NativeType.GetMethod("Free", BindingFlags.Static | BindingFlags.NonPublic);
tuple.nativeFree = freeNativeMethod?.CreateDelegate(typeof(Action<>).MakeGenericType(internalType));
} }
else else
{ {
@@ -1555,7 +1561,7 @@ namespace FlaxEngine.Interop
} }
tuple = unboxers.GetOrAdd(type, tuple); tuple = unboxers.GetOrAdd(type, tuple);
} }
return tuple.deleg(value, tuple.toNativeDeleg); return tuple.deleg(value, tuple.toNativeConverter, tuple.nativeFree);
} }
private static void PinValue(object value) private static void PinValue(object value)
@@ -1569,12 +1575,15 @@ namespace FlaxEngine.Interop
handle = GCHandle.Alloc(value, GCHandleType.Pinned); handle = GCHandle.Alloc(value, GCHandleType.Pinned);
} }
private static IntPtr PinValue<T>(T value) where T : struct private static IntPtr PinValue<T>(T value, out uint index) where T : struct
{ {
// Store the converted value in unmanaged memory so it will not be relocated by the garbage collector. // Store the converted value in unmanaged memory so it will not be relocated by the garbage collector.
int size = TypeHelpers<T>.MarshalSize; int size = TypeHelpers<T>.MarshalSize;
uint index = Interlocked.Increment(ref pinnedAllocationsPointer) % (uint)pinnedAllocations.Length; index = Interlocked.Increment(ref pinnedAllocationsPointer) % (uint)pinnedAllocations.Length;
ref (IntPtr ptr, int size) alloc = ref pinnedAllocations[index]; ref (IntPtr ptr, int size) alloc = ref pinnedAllocations[index];
Action freeNativeDelegate = pinnedNativeFreeDelegates[index];
if (freeNativeDelegate != null)
freeNativeDelegate();
if (alloc.size < size) if (alloc.size < size)
{ {
if (alloc.ptr != IntPtr.Zero) if (alloc.ptr != IntPtr.Zero)
@@ -1586,7 +1595,7 @@ namespace FlaxEngine.Interop
return alloc.ptr; return alloc.ptr;
} }
private static IntPtr UnboxPointer<T>(object value, object converter) where T : struct private static IntPtr UnboxPointer<T>(object value, object toNativeConverter, object nativeFreeDelegate) where T : struct
{ {
if (RuntimeHelpers.IsReferenceOrContainsReferences<T>()) // Cannot pin structure with references if (RuntimeHelpers.IsReferenceOrContainsReferences<T>()) // Cannot pin structure with references
return IntPtr.Zero; return IntPtr.Zero;
@@ -1594,11 +1603,16 @@ namespace FlaxEngine.Interop
return new IntPtr(Unsafe.AsPointer(ref Unsafe.Unbox<T>(value))); return new IntPtr(Unsafe.AsPointer(ref Unsafe.Unbox<T>(value)));
} }
private static IntPtr UnboxPointerWithConverter<T, TInternal>(object value, object converter) where T : struct private static IntPtr UnboxPointerWithConverter<T, TInternal>(
object value, object nativeConverterDelegate, object nativeFreeDelegate) where T : struct
where TInternal : struct where TInternal : struct
{ {
ToNativeDelegate<T, TInternal> toNative = Unsafe.As<ToNativeDelegate<T, TInternal>>(converter); ToNativeDelegate<T, TInternal> toNative = Unsafe.As<ToNativeDelegate<T, TInternal>>(nativeConverterDelegate);
return PinValue<TInternal>(toNative(Unsafe.Unbox<T>(value))); TInternal nativeValue = toNative(Unsafe.Unbox<T>(value));
IntPtr pinnedPtr = PinValue<TInternal>(nativeValue, out uint index);
Action<TInternal> freeNative = Unsafe.As<Action<TInternal>>(nativeFreeDelegate);
//pinnedNativeFreeDelegates[index] = () => freeNative(nativeValue);
return pinnedPtr;
} }
} }
@@ -1743,25 +1757,37 @@ namespace FlaxEngine.Interop
internal static void InitMethods() internal static void InitMethods()
{ {
return;
MakeNewCustomDelegateFunc = MakeNewCustomDelegateFunc =
typeof(Expression).Assembly.GetType("System.Linq.Expressions.Compiler.DelegateHelpers") typeof(Expression).Assembly.GetType("System.Linq.Expressions.Compiler.DelegateHelpers")
.GetMethod("MakeNewCustomDelegate", BindingFlags.NonPublic | BindingFlags.Static).CreateDelegate<Func<Type[], Type>>(); .GetMethod("MakeNewCustomDelegate", BindingFlags.NonPublic | BindingFlags.Static).CreateDelegate<Func<Type[], Type>>();
MakeNewCustomDelegateFunc(new[] { typeof(void) });
#if FLAX_EDITOR #if FLAX_EDITOR
// Load System.Linq.Expressions assembly to collectible ALC. // Load System.Linq.Expressions assembly to collectible ALC.
// The dynamic assembly where delegates are stored is cached in the DelegateHelpers class, so we should // The dynamic assembly where delegates are stored is cached in the DelegateHelpers class, so we should
// use the DelegateHelpers in collectible ALC to make sure the delegates are also stored in the same ALC. // use the DelegateHelpers in collectible ALC to make sure the delegates are also stored in the same ALC.
Assembly assembly = scriptingAssemblyLoadContext.LoadFromAssemblyPath(typeof(Expression).Assembly.Location); //using var _ = scriptingAssemblyLoadContext.EnterContextualReflection();
//Debug.Logger.LogHandler.LogWrite(LogType.Warning, "InitMethods.");
var asdfa = AssemblyLoadContext.All.ToArray();
var asma = typeof(Expression).Assembly;
var loc = asma.Location;
using FileStream stream = new FileStream(loc, FileMode.Open, FileAccess.Read);
Assembly assembly = scriptingAssemblyLoadContext.LoadFromAssemblyPath(loc);
MakeNewCustomDelegateFuncCollectible = MakeNewCustomDelegateFuncCollectible =
assembly.GetType("System.Linq.Expressions.Compiler.DelegateHelpers") assembly.GetType("System.Linq.Expressions.Compiler.DelegateHelpers")
.GetMethod("MakeNewCustomDelegate", BindingFlags.NonPublic | BindingFlags.Static).CreateDelegate<Func<Type[], Type>>(); .GetMethod("MakeNewCustomDelegate", BindingFlags.NonPublic | BindingFlags.Static).CreateDelegate<Func<Type[], Type>>();
// Create dummy delegates to force the dynamic Snippets assembly to be loaded in correcet ALCs Debug.Logger.LogHandler.LogWrite(LogType.Warning, "InitMethods ok .");
MakeNewCustomDelegateFunc(new[] { typeof(void) });
{ {
// Ensure the new delegate is placed in the collectible ALC // Ensure any future delegates is placed in the collectible ALC
using var ctx = scriptingAssemblyLoadContext.EnterContextualReflection(); using var ctx = scriptingAssemblyLoadContext.EnterContextualReflection();
MakeNewCustomDelegateFuncCollectible(new[] { typeof(void) }); var ret = MakeNewCustomDelegateFuncCollectible(new[] { typeof(void) });
Assert.IsTrue(ret.IsCollectible);
} }
#endif #endif
} }
@@ -1785,6 +1811,102 @@ namespace FlaxEngine.Interop
#endif #endif
} }
internal static class ObjectArrayPool
{
[ThreadStatic]
private static List<(bool InUse, object[] Array)> _pool;
[ThreadStatic]
private static List<(bool InUse, object[] Array)> _pool2;
/// <summary>
/// Rents an array from the pool.
/// </summary>
/// <param name="size">Exact size of the array.</param>
internal static object[] Rent(int size)
{
if (size == 0)
return Array.Empty<object>();
if (_pool == null)
_pool = new(16);
foreach (ref var tuple in CollectionsMarshal.AsSpan(_pool))
{
if (tuple.InUse)
continue;
if (tuple.Array.Length != size)
continue;
tuple.InUse = true;
return tuple.Array;
}
var newTuple = (InUse: true, Array: new object[size]);
_pool.Add(newTuple);
return newTuple.Array;
}
internal static object[] Rent2()
{
if (_pool2 == null)
_pool2 = new(16);
foreach (ref var tuple in CollectionsMarshal.AsSpan(_pool2))
{
if (tuple.InUse)
continue;
tuple.InUse = true;
return tuple.Array;
}
var newTuple = (InUse: true, Array: new object[8]);
_pool2.Add(newTuple);
return newTuple.Array;
}
/// <summary>
/// Returns the rented object array back to the pool.
/// </summary>
/// <param name="array">The array rented from the pool.</param>
internal static void Return(object[] array)
{
if (array.Length == 0)
return;
foreach (ref var tuple in CollectionsMarshal.AsSpan(_pool))
{
if (tuple.Array != array)
continue;
tuple.InUse = false;
for (int i = 0; i < array.Length; i++)
array[i] = null;
return;
}
throw new NativeInteropException("Tried to free non-pooled object array as pooled ManagedArray");
}
internal static void Return2(object[] array)
{
foreach (ref var tuple in CollectionsMarshal.AsSpan(_pool2))
{
if (tuple.Array != array)
continue;
tuple.InUse = false;
for (int i = 0; i < array.Length; i++)
array[i] = null;
return;
}
throw new NativeInteropException("Tried to free non-pooled object array as pooled ManagedArray");
}
}
#if !USE_AOT #if !USE_AOT
/// <summary> /// <summary>
/// Wrapper class for invoking function pointers from unmanaged code. /// Wrapper class for invoking function pointers from unmanaged code.
@@ -1792,13 +1914,18 @@ namespace FlaxEngine.Interop
internal class ThunkContext internal class ThunkContext
{ {
internal MethodInfo method; internal MethodInfo method;
internal MethodInvoker invoker;
internal Type[] parameterTypes; internal Type[] parameterTypes;
internal Invoker.InvokeThunkDelegate methodDelegate; internal Invoker.InvokeThunkDelegate methodDelegate;
internal object methodDelegateContext; internal object methodDelegateContext;
internal static object[] objectPool = new object[128];
internal static int objectPoolIndex = 0;
internal ThunkContext(MethodInfo method) internal ThunkContext(MethodInfo method)
{ {
this.method = method; this.method = method;
invoker = MethodInvoker.Create(method);
parameterTypes = method.GetParameterTypes(); parameterTypes = method.GetParameterTypes();
// Thunk delegates don't support IsByRef parameters (use generic invocation that handles 'out' and 'ref' prams) // Thunk delegates don't support IsByRef parameters (use generic invocation that handles 'out' and 'ref' prams)
@@ -1826,6 +1953,7 @@ namespace FlaxEngine.Interop
genericParamTypes.Add(type); genericParamTypes.Add(type);
} }
#if false
string invokerTypeName = $"{typeof(Invoker).FullName}+Invoker{(method.IsStatic ? "Static" : "")}{(method.ReturnType != typeof(void) ? "Ret" : "NoRet")}{parameterTypes.Length}{(genericParamTypes.Count > 0 ? "`" + genericParamTypes.Count : "")}"; string invokerTypeName = $"{typeof(Invoker).FullName}+Invoker{(method.IsStatic ? "Static" : "")}{(method.ReturnType != typeof(void) ? "Ret" : "NoRet")}{parameterTypes.Length}{(genericParamTypes.Count > 0 ? "`" + genericParamTypes.Count : "")}";
Type invokerType = Type.GetType(invokerTypeName); Type invokerType = Type.GetType(invokerTypeName);
if (invokerType != null) if (invokerType != null)
@@ -1838,11 +1966,13 @@ namespace FlaxEngine.Interop
if (methodDelegate != null) if (methodDelegate != null)
Assert.IsTrue(methodDelegateContext != null); Assert.IsTrue(methodDelegateContext != null);
#endif
} }
public IntPtr InvokeThunk(ManagedHandle instanceHandle, IntPtr param1, IntPtr param2, IntPtr param3, IntPtr param4, IntPtr param5, IntPtr param6, IntPtr param7) public IntPtr InvokeThunk(ManagedHandle instanceHandle, IntPtr param1, IntPtr param2, IntPtr param3, IntPtr param4, IntPtr param5, IntPtr param6, IntPtr param7)
{ {
IntPtr* nativePtrs = stackalloc IntPtr[] { param1, param2, param3, param4, param5, param6, param7 }; IntPtr* nativePtrs = stackalloc IntPtr[] { param1, param2, param3, param4, param5, param6, param7 };
#if false
if (methodDelegate != null) if (methodDelegate != null)
{ {
IntPtr returnValue; IntPtr returnValue;
@@ -1861,36 +1991,69 @@ namespace FlaxEngine.Interop
return returnValue; return returnValue;
} }
else else
#endif
{ {
// The parameters are wrapped (boxed) in GCHandles // The parameters are wrapped (boxed) in GCHandles
object returnObject; object returnObject;
int numParams = parameterTypes.Length; int numParams = parameterTypes.Length;
object[] methodParameters = new object[numParams]; //object[] methodParameters = ObjectArrayPool.Rent(numParams);//new object[numParams]; // TODO: object array pool
if (objectPoolIndex + numParams > 128)
objectPoolIndex = 0;
var paramSpan = objectPool.AsSpan(objectPoolIndex, numParams);
objectPoolIndex += numParams;
for (int i = 0; i < numParams; i++) for (int i = 0; i < numParams; i++)
{ {
IntPtr nativePtr = nativePtrs[i]; IntPtr nativePtr = nativePtrs[i];
object managed = null;
if (nativePtr != IntPtr.Zero) if (nativePtr != IntPtr.Zero)
{ {
object managed = null;
Type type = parameterTypes[i]; Type type = parameterTypes[i];
Type elementType = type.GetElementType();
if (type.IsByRef) if (type.IsByRef)
{ {
// References use indirection to support value returning // References use indirection to support value returning
nativePtr = Unsafe.Read<IntPtr>(nativePtr.ToPointer()); nativePtr = Unsafe.Read<IntPtr>(nativePtr.ToPointer());
type = elementType; type = type.GetElementType();
} }
if (type.IsArray) if (type.IsArray)
managed = MarshalToManaged(nativePtr, type); // Array might be in internal format of custom structs so unbox if need to managed = MarshalToManaged(nativePtr, type); // Array might be in internal format of custom structs so unbox if need to
else if (type == typeof(Type))
managed = Unsafe.As<TypeHolder>(ManagedHandle.FromIntPtr(nativePtr).Target).type;
else else
managed = ManagedHandle.FromIntPtr(nativePtr).Target; managed = ManagedHandle.FromIntPtr(nativePtr).Target;
paramSpan[i] = managed;
} }
methodParameters[i] = managed;
} }
try try
{ {
returnObject = method.Invoke(instanceHandle.IsAllocated ? instanceHandle.Target : null, methodParameters); returnObject = invoker.Invoke(instanceHandle.IsAllocated ? instanceHandle.Target : null, paramSpan);
//returnObject = method.Invoke(instanceHandle.IsAllocated ? instanceHandle.Target : null, methodParameters);
// Marshal reference parameters back to original unmanaged references
for (int i = 0; i < numParams; i++)
{
IntPtr nativePtr = nativePtrs[i];
Type type = parameterTypes[i];
if (nativePtr != IntPtr.Zero && type.IsByRef)
{
object managed = paramSpan[i];
type = type.GetElementType();
if (managed == null)
Unsafe.Write<IntPtr>(nativePtr.ToPointer(), IntPtr.Zero);
else if (type.IsArray)
MarshalToNative(managed, nativePtr, type);
else
{
var ptr = nativePtr.ToPointer();
if (type == typeof(Type))
managed = GetTypeHolder(Unsafe.As<Type>(managed));
var handle = ManagedHandle.Alloc(managed/*, GCHandleType.Weak*/);
var handleptr = ManagedHandle.ToIntPtr(handle);
Unsafe.Write<IntPtr>(ptr, handleptr);
//Unsafe.Write<IntPtr>(nativePtr.ToPointer(), ManagedHandle.ToIntPtr(ManagedHandle.Alloc(managed, GCHandleType.Weak)));
}
}
}
} }
catch (Exception exception) catch (Exception exception)
{ {
@@ -1900,26 +2063,13 @@ namespace FlaxEngine.Interop
Unsafe.Write<IntPtr>(exceptionPtr.ToPointer(), ManagedHandle.ToIntPtr(exception, GCHandleType.Weak)); Unsafe.Write<IntPtr>(exceptionPtr.ToPointer(), ManagedHandle.ToIntPtr(exception, GCHandleType.Weak));
return IntPtr.Zero; return IntPtr.Zero;
} }
finally
// Marshal reference parameters back to original unmanaged references {
//ObjectArrayPool.Return(methodParameters);
for (int i = 0; i < numParams; i++) for (int i = 0; i < numParams; i++)
{ paramSpan[i] = null;
IntPtr nativePtr = nativePtrs[i];
Type type = parameterTypes[i];
object managed = methodParameters[i];
if (nativePtr != IntPtr.Zero && type.IsByRef)
{
type = type.GetElementType();
if (managed == null)
Unsafe.Write<IntPtr>(nativePtr.ToPointer(), IntPtr.Zero);
else if (type.IsArray)
MarshalToNative(managed, nativePtr, type);
else
Unsafe.Write<IntPtr>(nativePtr.ToPointer(), ManagedHandle.ToIntPtr(ManagedHandle.Alloc(managed, GCHandleType.Weak)));
}
} }
// Return value
return Invoker.MarshalReturnValueThunkGeneric(method.ReturnType, returnObject); return Invoker.MarshalReturnValueThunkGeneric(method.ReturnType, returnObject);
} }
} }

View File

@@ -450,7 +450,7 @@ public:
/// <summary> /// <summary>
/// The high-level renderer context. Used to collect the draw calls for the scene rendering. Can be used to perform a custom rendering. /// The high-level renderer context. Used to collect the draw calls for the scene rendering. Can be used to perform a custom rendering.
/// </summary> /// </summary>
API_STRUCT(NoDefault) struct RenderContext API_STRUCT(NoDefault) struct FLAXENGINE_API RenderContext
{ {
DECLARE_SCRIPTING_TYPE_MINIMAL(RenderContext); DECLARE_SCRIPTING_TYPE_MINIMAL(RenderContext);
@@ -491,7 +491,7 @@ API_STRUCT(NoDefault) struct RenderContext
/// <summary> /// <summary>
/// The high-level renderer context batch that encapsulates multiple rendering requests within a single task (eg. optimize main view scene rendering and shadow projections at once). /// The high-level renderer context batch that encapsulates multiple rendering requests within a single task (eg. optimize main view scene rendering and shadow projections at once).
/// </summary> /// </summary>
API_STRUCT(NoDefault) struct RenderContextBatch API_STRUCT(NoDefault) struct FLAXENGINE_API RenderContextBatch
{ {
DECLARE_SCRIPTING_TYPE_MINIMAL(RenderContextBatch); DECLARE_SCRIPTING_TYPE_MINIMAL(RenderContextBatch);

View File

@@ -553,7 +553,7 @@ void GPUTextureDX11::initHandles()
if (useDSV && useSRV && PixelFormatExtensions::HasStencil(format)) if (useDSV && useSRV && PixelFormatExtensions::HasStencil(format))
{ {
PixelFormat stencilFormat; PixelFormat stencilFormat;
switch (_dxgiFormatDSV) switch (static_cast<PixelFormat>(_dxgiFormatDSV))
{ {
case PixelFormat::D24_UNorm_S8_UInt: case PixelFormat::D24_UNorm_S8_UInt:
srDesc.Format = DXGI_FORMAT_X24_TYPELESS_G8_UINT; srDesc.Format = DXGI_FORMAT_X24_TYPELESS_G8_UINT;

View File

@@ -210,7 +210,7 @@ public:
/// <param name="device">The graphics device.</param> /// <param name="device">The graphics device.</param>
/// <param name="name">The resource name.</param> /// <param name="name">The resource name.</param>
GPUResourceDX12(GPUDeviceDX12* device, const StringView& name) GPUResourceDX12(GPUDeviceDX12* device, const StringView& name)
: GPUResourceBase(device, name) : GPUResourceBase<GPUDeviceDX12, BaseType>(device, name)
{ {
} }
}; };

View File

@@ -732,7 +732,7 @@ void GPUTextureDX12::initHandles()
if (useDSV && useSRV && PixelFormatExtensions::HasStencil(format)) if (useDSV && useSRV && PixelFormatExtensions::HasStencil(format))
{ {
PixelFormat stencilFormat; PixelFormat stencilFormat;
switch (_dxgiFormatDSV) switch (static_cast<PixelFormat>(_dxgiFormatDSV))
{ {
case PixelFormat::D24_UNorm_S8_UInt: case PixelFormat::D24_UNorm_S8_UInt:
srDesc.Format = DXGI_FORMAT_X24_TYPELESS_G8_UINT; srDesc.Format = DXGI_FORMAT_X24_TYPELESS_G8_UINT;

View File

@@ -194,7 +194,8 @@ CultureInfo MUtils::ToNative(void* value)
if (lcidProperty && lcidProperty->GetGetMethod()) if (lcidProperty && lcidProperty->GetGetMethod())
{ {
MObject* lcidObj = lcidProperty->GetGetMethod()->Invoke(value, nullptr, nullptr); MObject* lcidObj = lcidProperty->GetGetMethod()->Invoke(value, nullptr, nullptr);
lcid = *(int32*)MCore::Object::Unbox(lcidObj); PLATFORM_DEBUG_BREAK;
MCore::Object::Unbox(lcidObj, &lcid);
} }
} }
#endif #endif

View File

@@ -74,7 +74,7 @@
MObject* exception = nullptr; \ MObject* exception = nullptr; \
auto resultObj = _method_##name->Invoke(GetManagedInstance(), params, &exception); \ auto resultObj = _method_##name->Invoke(GetManagedInstance(), params, &exception); \
if (resultObj) \ if (resultObj) \
result = (DragDropEffect)MUtils::Unbox<int32>(resultObj); \ result = (DragDropEffect)MUtils::Unbox<int32>(resultObj, true); \
END_INVOKE_EVENT(name) END_INVOKE_EVENT(name)
#else #else
#define INVOKE_EVENT(name, paramsCount, param0, param1, param2) #define INVOKE_EVENT(name, paramsCount, param0, param1, param2)

View File

@@ -11,6 +11,7 @@
#include "Engine/Platform/CreateProcessSettings.h" #include "Engine/Platform/CreateProcessSettings.h"
#include "Engine/Platform/WindowsManager.h" #include "Engine/Platform/WindowsManager.h"
#include "Engine/Platform/SDL/SDLInput.h" #include "Engine/Platform/SDL/SDLInput.h"
#include "Engine/Engine/CommandLine.h"
#include <SDL3/SDL_hints.h> #include <SDL3/SDL_hints.h>
#include <SDL3/SDL_init.h> #include <SDL3/SDL_init.h>
@@ -317,11 +318,11 @@ Window* SDLPlatform::CreateWindow(const CreateWindowSettings& settings)
return New<SDLWindow>(settings); return New<SDLWindow>(settings);
} }
bool ReadStream(SDL_IOStream*& stream, char* buffer, int32 bufferLength, int32& bufferPosition, LogType logType, CreateProcessSettings& settings) bool ReadStream(SDL_IOStream*& stream, char* buffer, int64 bufferLength, int64& bufferPosition, LogType logType, CreateProcessSettings& settings)
{ {
bool flushBuffer = false; bool flushBuffer = false;
bool success = true; bool success = true;
int32 read = SDL_ReadIO(stream, buffer + bufferPosition, bufferLength - bufferPosition - 1); auto read = SDL_ReadIO(stream, buffer + bufferPosition, bufferLength - bufferPosition - 1);
if (read == 0) if (read == 0)
{ {
SDL_IOStatus status = SDL_GetIOStatus(stream); SDL_IOStatus status = SDL_GetIOStatus(stream);
@@ -335,8 +336,8 @@ bool ReadStream(SDL_IOStream*& stream, char* buffer, int32 bufferLength, int32&
} }
else else
{ {
int32 startPosition = bufferPosition; int64 startPosition = bufferPosition;
bufferPosition += read; bufferPosition += (int64)read;
if (bufferPosition == bufferLength - 1) if (bufferPosition == bufferLength - 1)
{ {
flushBuffer = true; flushBuffer = true;
@@ -344,7 +345,7 @@ bool ReadStream(SDL_IOStream*& stream, char* buffer, int32 bufferLength, int32&
} }
else else
{ {
for (int i = startPosition; i < bufferPosition; ++i) for (int64 i = startPosition; i < bufferPosition; ++i)
{ {
if (buffer[i] == '\n') if (buffer[i] == '\n')
{ {
@@ -357,13 +358,13 @@ bool ReadStream(SDL_IOStream*& stream, char* buffer, int32 bufferLength, int32&
if (flushBuffer) if (flushBuffer)
{ {
int32 start = 0; int64 start = 0;
for (int i = 0; i < bufferPosition; ++i) for (int64 i = 0; i < bufferPosition; ++i)
{ {
if (buffer[i] != '\n') if (buffer[i] != '\n')
continue; continue;
String str(&buffer[start], i - start + 1); String str(&buffer[start], (int32)(i - start + 1));
#if LOG_ENABLE #if LOG_ENABLE
if (settings.LogOutput) if (settings.LogOutput)
Log::Logger::Write(logType, StringView(str.Get(), str.Length() - 1)); Log::Logger::Write(logType, StringView(str.Get(), str.Length() - 1));
@@ -372,7 +373,7 @@ bool ReadStream(SDL_IOStream*& stream, char* buffer, int32 bufferLength, int32&
settings.Output.Add(str.Get(), str.Length()); settings.Output.Add(str.Get(), str.Length());
start = i + 1; start = i + 1;
} }
int32 length = bufferPosition - start; int64 length = bufferPosition - start;
if (length > 0) if (length > 0)
{ {
// TODO: Use memmove here? Overlapped memory regions with memcpy is undefined behaviour // TODO: Use memmove here? Overlapped memory regions with memcpy is undefined behaviour
@@ -396,13 +397,7 @@ int32 SDLPlatform::CreateProcess(CreateProcessSettings& settings)
int32 result = 0; int32 result = 0;
const bool captureStdOut = settings.LogOutput || settings.SaveOutput; const bool captureStdOut = settings.LogOutput || settings.SaveOutput;
const StringAnsi cmdLine = StringAnsi::Format("\"{0}\" {1}", StringAnsi(settings.FileName), StringAnsi(settings.Arguments)); const StringAnsi cmdLine = StringAnsi::Format("\"{0}\" {1}", StringAnsi(settings.FileName), StringAnsi(settings.Arguments));
const char* cmd[] = { "sh", "-c", cmdLine.Get(), (char *)0 };
StringAnsi workingDirectory(settings.WorkingDirectory); StringAnsi workingDirectory(settings.WorkingDirectory);
#if PLATFORM_WINDOWS
bool background = !settings.WaitForEnd || settings.HiddenWindow; // This also hides the window on Windows
#else
bool background = !settings.WaitForEnd;
#endif
// Populate environment with current values from parent environment. // Populate environment with current values from parent environment.
// SDL does not populate the environment with the latest values but with a snapshot captured during initialization. // SDL does not populate the environment with the latest values but with a snapshot captured during initialization.
@@ -414,8 +409,27 @@ int32 SDLPlatform::CreateProcess(CreateProcessSettings& settings)
for (auto iter = settings.Environment.Begin(); iter != settings.Environment.End(); ++iter) for (auto iter = settings.Environment.Begin(); iter != settings.Environment.End(); ++iter)
SDL_SetEnvironmentVariable(env, StringAnsi(iter->Key).Get(), StringAnsi(iter->Value).Get(), true); SDL_SetEnvironmentVariable(env, StringAnsi(iter->Key).Get(), StringAnsi(iter->Value).Get(), true);
// Parse argument list with possible quotes included
Array<StringAnsi> arguments;
arguments.Add(StringAnsi(settings.FileName));
if (CommandLine::ParseArguments(settings.Arguments, arguments))
{
LOG(Error, "Failed to parse arguments for process {}: '{}'", settings.FileName.Get(), settings.Arguments.Get());
return -1;
}
Array<const char*> cmd;
for (const StringAnsi& str : arguments)
cmd.Add(str.Get());
cmd.Add((const char*)0);
#if PLATFORM_WINDOWS
bool background = !settings.WaitForEnd || settings.HiddenWindow; // This also hides the window on Windows
#else
bool background = !settings.WaitForEnd;
#endif
SDL_PropertiesID props = SDL_CreateProperties(); SDL_PropertiesID props = SDL_CreateProperties();
SDL_SetPointerProperty(props, SDL_PROP_PROCESS_CREATE_ARGS_POINTER, cmd); SDL_SetPointerProperty(props, SDL_PROP_PROCESS_CREATE_ARGS_POINTER, cmd.Get());
SDL_SetPointerProperty(props, SDL_PROP_PROCESS_CREATE_ENVIRONMENT_POINTER, env); SDL_SetPointerProperty(props, SDL_PROP_PROCESS_CREATE_ENVIRONMENT_POINTER, env);
SDL_SetBooleanProperty(props, SDL_PROP_PROCESS_CREATE_BACKGROUND_BOOLEAN, background); SDL_SetBooleanProperty(props, SDL_PROP_PROCESS_CREATE_BACKGROUND_BOOLEAN, background);
if (workingDirectory.HasChars()) if (workingDirectory.HasChars())
@@ -430,12 +444,12 @@ int32 SDLPlatform::CreateProcess(CreateProcessSettings& settings)
SDL_DestroyEnvironment(env); SDL_DestroyEnvironment(env);
if (process == nullptr) if (process == nullptr)
{ {
LOG(Error, "Failed to run process {}: {}", String(settings.FileName).Get(), String(SDL_GetError())); LOG(Error, "Failed to run process {}: {}", settings.FileName.Get(), String(SDL_GetError()));
return -1; return -1;
} }
props = SDL_GetProcessProperties(process); props = SDL_GetProcessProperties(process);
int32 pid = SDL_GetNumberProperty(props, SDL_PROP_PROCESS_PID_NUMBER, 0); int64 pid = SDL_GetNumberProperty(props, SDL_PROP_PROCESS_PID_NUMBER, 0);
SDL_IOStream* stdoutStream = nullptr; SDL_IOStream* stdoutStream = nullptr;
SDL_IOStream* stderrStream = nullptr; SDL_IOStream* stderrStream = nullptr;
if (captureStdOut) if (captureStdOut)
@@ -446,9 +460,9 @@ int32 SDLPlatform::CreateProcess(CreateProcessSettings& settings)
// Handle process output in realtime // Handle process output in realtime
char stdoutBuffer[2049]; char stdoutBuffer[2049];
int32 stdoutPosition = 0; int64 stdoutPosition = 0;
char stderrBuffer[2049]; char stderrBuffer[2049];
int32 stderrPosition = 0; int64 stderrPosition = 0;
while (stdoutStream != nullptr && stderrStream != nullptr) while (stdoutStream != nullptr && stderrStream != nullptr)
{ {
if (stdoutStream != nullptr && !ReadStream(stdoutStream, stdoutBuffer, sizeof(stdoutBuffer), stdoutPosition, LogType::Info, settings)) if (stdoutStream != nullptr && !ReadStream(stdoutStream, stdoutBuffer, sizeof(stdoutBuffer), stdoutPosition, LogType::Info, settings))

View File

@@ -1332,7 +1332,7 @@ bool ManagedBinaryModule::InvokeMethod(void* method, const Variant& instance, Sp
} }
// Unbox result // Unbox result
result = MUtils::UnboxVariant(resultObject); result = MUtils::UnboxVariant(resultObject, true);
#if 0 #if 0
// Helper method invocations logging // Helper method invocations logging
@@ -1366,7 +1366,7 @@ bool ManagedBinaryModule::InvokeMethod(void* method, const Variant& instance, Sp
paramValue.SetString(MUtils::ToString((MString*)param)); paramValue.SetString(MUtils::ToString((MString*)param));
break; break;
case VariantType::Object: case VariantType::Object:
paramValue = MUtils::UnboxVariant((MObject*)param); paramValue = MUtils::UnboxVariant((MObject*)param, true);
break; break;
case VariantType::Structure: case VariantType::Structure:
{ {
@@ -1383,7 +1383,7 @@ bool ManagedBinaryModule::InvokeMethod(void* method, const Variant& instance, Sp
{ {
MType* paramType = mMethod->GetParameterType(paramIdx); MType* paramType = mMethod->GetParameterType(paramIdx);
if (MCore::Type::IsReference(paramType) && MCore::Type::GetType(paramType) == MTypes::Object) if (MCore::Type::IsReference(paramType) && MCore::Type::GetType(paramType) == MTypes::Object)
paramValue = MUtils::UnboxVariant((MObject*)outParams[paramIdx]); paramValue = MUtils::UnboxVariant((MObject*)outParams[paramIdx], true);
break; break;
} }
} }
@@ -1527,7 +1527,7 @@ bool ManagedBinaryModule::GetFieldValue(void* field, const Variant& instance, Va
const auto mField = (MField*)field; const auto mField = (MField*)field;
resultObject = mField->GetValueBoxed(instanceObject); resultObject = mField->GetValueBoxed(instanceObject);
} }
result = MUtils::UnboxVariant(resultObject); result = MUtils::UnboxVariant(resultObject, true);
return false; return false;
#else #else
return true; return true;

View File

@@ -69,6 +69,7 @@ public:
{ {
static MObject* Box(void* value, const MClass* klass); static MObject* Box(void* value, const MClass* klass);
static void* Unbox(MObject* obj); static void* Unbox(MObject* obj);
static void Unbox(MObject* obj, void* dest);
static MObject* New(const MClass* klass); static MObject* New(const MClass* klass);
static void Init(MObject* obj); static void Init(MObject* obj);
static MClass* GetClass(MObject* obj); static MClass* GetClass(MObject* obj);
@@ -84,6 +85,7 @@ public:
static MString* GetEmpty(MDomain* domain = nullptr); static MString* GetEmpty(MDomain* domain = nullptr);
static MString* New(const char* str, int32 length, MDomain* domain = nullptr); static MString* New(const char* str, int32 length, MDomain* domain = nullptr);
static MString* New(const Char* str, int32 length, MDomain* domain = nullptr); static MString* New(const Char* str, int32 length, MDomain* domain = nullptr);
static void Free(MString* obj);
static StringView GetChars(MString* obj); static StringView GetChars(MString* obj);
}; };
@@ -93,6 +95,7 @@ public:
struct FLAXENGINE_API Array struct FLAXENGINE_API Array
{ {
static MArray* New(const MClass* elementKlass, int32 length); static MArray* New(const MClass* elementKlass, int32 length);
static void Free(const MArray* array);
static MClass* GetClass(MClass* elementKlass); static MClass* GetClass(MClass* elementKlass);
static MClass* GetArrayClass(const MArray* obj); static MClass* GetArrayClass(const MArray* obj);
static int32 GetLength(const MArray* obj); static int32 GetLength(const MArray* obj);

View File

@@ -304,7 +304,7 @@ MTypeObject* MUtils::BoxVariantType(const VariantType& value)
return INTERNAL_TYPE_GET_OBJECT(mType); return INTERNAL_TYPE_GET_OBJECT(mType);
} }
Variant MUtils::UnboxVariant(MObject* value) Variant MUtils::UnboxVariant(MObject* value, bool releaseHandle)
{ {
if (value == nullptr) if (value == nullptr)
return Variant::Null; return Variant::Null;
@@ -313,7 +313,10 @@ Variant MUtils::UnboxVariant(MObject* value)
MType* mType = klass->GetType(); MType* mType = klass->GetType();
const MTypes mTypes = MCore::Type::GetType(mType); const MTypes mTypes = MCore::Type::GetType(mType);
void* unboxed = MCore::Object::Unbox(value);
Variant storage;
MCore::Object::Unbox(value, &storage.AsData);
void* unboxed = &storage.AsData;
// Fast type detection for in-built types // Fast type detection for in-built types
switch (mTypes) switch (mTypes)
@@ -345,7 +348,11 @@ Variant MUtils::UnboxVariant(MObject* value)
case MTypes::R8: case MTypes::R8:
return *static_cast<double*>(unboxed); return *static_cast<double*>(unboxed);
case MTypes::String: case MTypes::String:
{
if (releaseHandle)
MUtils::FreeManaged<String>(value);
return Variant(MUtils::ToString((MString*)value)); return Variant(MUtils::ToString((MString*)value));
}
case MTypes::Ptr: case MTypes::Ptr:
return *static_cast<void**>(unboxed); return *static_cast<void**>(unboxed);
case MTypes::ValueType: case MTypes::ValueType:
@@ -400,6 +407,8 @@ Variant MUtils::UnboxVariant(MObject* value)
{ {
Variant v; Variant v;
v.SetBlob(ptr, MCore::Array::GetLength((MArray*)value)); v.SetBlob(ptr, MCore::Array::GetLength((MArray*)value));
if (releaseHandle)
MUtils::FreeManaged<Array<byte>>(value);
return v; return v;
} }
const StringAnsiView fullname = arrayClass->GetFullName(); const StringAnsiView fullname = arrayClass->GetFullName();
@@ -486,13 +495,16 @@ Variant MUtils::UnboxVariant(MObject* value)
// TODO: optimize this for large arrays to prevent multiple AllocStructure calls in Variant::SetType by using computed struct type // 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++) for (int32 i = 0; i < array.Count(); i++)
{ {
PLATFORM_DEBUG_BREAK; // FIXME
auto& a = array[i]; auto& a = array[i];
a.SetType(elementType); a.SetType(elementType);
void* managed = (byte*)ptr + elementSize * i; void* managed = (byte*)ptr + elementSize * i;
// TODO: optimize structures unboxing to not require MObject* but raw managed value data to prevent additional boxing here // TODO: optimize structures unboxing to not require MObject* but raw managed value data to prevent additional boxing here
MObject* boxed = MCore::Object::New(elementClass); //MObject* boxed = MCore::Object::New(elementClass);
Platform::MemoryCopy(MCore::Object::Unbox(boxed), managed, elementSize); //Variant storage;
type.Struct.Unbox(a.AsBlob.Data, boxed); MCore::Object::Unbox((MObject*)managed, &a.AsBlob.Data);
//Platform::MemoryCopy(MCore::Object::Unbox(boxed), managed, elementSize);
//type.Struct.Unbox(a.AsBlob.Data, boxed);
} }
break; break;
} }
@@ -508,7 +520,10 @@ Variant MUtils::UnboxVariant(MObject* value)
{ {
// Array of Objects // Array of Objects
for (int32 i = 0; i < array.Count(); i++) for (int32 i = 0; i < array.Count(); i++)
array[i] = UnboxVariant(((MObject**)ptr)[i]); array[i] = UnboxVariant(((MObject**)ptr)[i], releaseHandle);
if (releaseHandle)
MUtils::FreeManaged<Array<byte>>(value);
} }
return v; return v;
} }
@@ -527,10 +542,13 @@ Variant MUtils::UnboxVariant(MObject* value)
{ {
MObject* keyManaged = managedKeysPtr[i]; MObject* keyManaged = managedKeysPtr[i];
MObject* valueManaged = managed.GetValue(keyManaged); MObject* valueManaged = managed.GetValue(keyManaged);
native.Add(UnboxVariant(keyManaged), UnboxVariant(valueManaged)); native.Add(UnboxVariant(keyManaged, releaseHandle), UnboxVariant(valueManaged, releaseHandle));
} }
Variant v(MoveTemp(native)); Variant v(MoveTemp(native));
v.Type.SetTypeName(klass->GetFullName()); v.Type.SetTypeName(klass->GetFullName());
if (releaseHandle)
MCore::GCHandle::Free(*(MGCHandle*)&value);
//MUtils::FreeManaged<Dictionary<byte, byte>>(value);
return v; return v;
} }
break; break;
@@ -547,7 +565,10 @@ Variant MUtils::UnboxVariant(MObject* value)
Variant v; Variant v;
v.Type = MoveTemp(VariantType(VariantType::Enum, fullname)); v.Type = MoveTemp(VariantType(VariantType::Enum, fullname));
// TODO: what about 64-bit enum? use enum size with memcpy // TODO: what about 64-bit enum? use enum size with memcpy
v.AsUint64 = *static_cast<uint32*>(MCore::Object::Unbox(value)); PLATFORM_DEBUG_BREAK; // FIXME
MCore::Object::Unbox(value, &v.AsUint64);
if (releaseHandle)
MUtils::FreeManaged<byte>(value);
return v; return v;
} }
if (klass->IsValueType()) if (klass->IsValueType())
@@ -565,10 +586,12 @@ Variant MUtils::UnboxVariant(MObject* value)
type.Struct.Unbox(v.AsBlob.Data, value); type.Struct.Unbox(v.AsBlob.Data, value);
return v; return v;
} }
return Variant(value);
} }
return Variant(value); auto variant = Variant(value);
if (releaseHandle)
MUtils::FreeManaged<byte>(value);
return variant;
} }
MObject* MUtils::BoxVariant(const Variant& value) MObject* MUtils::BoxVariant(const Variant& value)
@@ -727,9 +750,10 @@ MObject* MUtils::BoxVariant(const Variant& value)
ASSERT(type.Type == ScriptingTypes::Structure); ASSERT(type.Type == ScriptingTypes::Structure);
for (int32 i = 0; i < array.Count(); i++) for (int32 i = 0; i < array.Count(); i++)
{ {
PLATFORM_DEBUG_BREAK; // FIXME
// TODO: optimize structures boxing to not return MObject* but use raw managed object to prevent additional boxing here // TODO: optimize structures boxing to not return MObject* but use raw managed object to prevent additional boxing here
MObject* boxed = type.Struct.Box(array[i].AsBlob.Data); MObject* boxed = type.Struct.Box(array[i].AsBlob.Data);
Platform::MemoryCopy(managedPtr + elementSize * i, MCore::Object::Unbox(boxed), elementSize); MCore::Object::Unbox(boxed, managedPtr + elementSize * i);
} }
break; break;
} }
@@ -1160,7 +1184,9 @@ void* MUtils::VariantToManagedArgPtr(Variant& value, MType* type, bool& failed)
auto& valueType = typeHandle.GetType(); auto& valueType = typeHandle.GetType();
if (valueType.ManagedClass == MCore::Type::GetClass(type)) if (valueType.ManagedClass == MCore::Type::GetClass(type))
{ {
return MCore::Object::Unbox(valueType.Struct.Box(value.AsBlob.Data)); PLATFORM_DEBUG_BREAK; // FIXME
MCore::Object::Unbox(valueType.Struct.Box(value.AsBlob.Data), &value.AsBlob.Data);
return &value.AsBlob.Data;
} }
LOG(Error, "Cannot marshal argument of type {0} as {1}", String(valueType.Fullname), MCore::Type::ToString(type)); LOG(Error, "Cannot marshal argument of type {0} as {1}", String(valueType.Fullname), MCore::Type::ToString(type));
} }
@@ -1171,9 +1197,11 @@ void* MUtils::VariantToManagedArgPtr(Variant& value, MType* type, bool& failed)
const ScriptingTypeHandle typeHandle = Scripting::FindScriptingType(fullname); const ScriptingTypeHandle typeHandle = Scripting::FindScriptingType(fullname);
if (typeHandle) if (typeHandle)
{ {
PLATFORM_DEBUG_BREAK; // FIXME
auto& valueType = typeHandle.GetType(); auto& valueType = typeHandle.GetType();
value.SetType(VariantType(VariantType::Structure, fullname)); value.SetType(VariantType(VariantType::Structure, fullname));
return MCore::Object::Unbox(valueType.Struct.Box(value.AsBlob.Data)); MCore::Object::Unbox(valueType.Struct.Box(value.AsBlob.Data), &value.AsBlob.Data);
return &value.AsBlob.Data;
} }
} }
} }

View File

@@ -39,7 +39,7 @@ namespace MUtils
extern FLAXENGINE_API MTypeObject* BoxScriptingTypeHandle(const ScriptingTypeHandle& value); extern FLAXENGINE_API MTypeObject* BoxScriptingTypeHandle(const ScriptingTypeHandle& value);
extern FLAXENGINE_API VariantType UnboxVariantType(MType* type); extern FLAXENGINE_API VariantType UnboxVariantType(MType* type);
extern FLAXENGINE_API MTypeObject* BoxVariantType(const VariantType& value); extern FLAXENGINE_API MTypeObject* BoxVariantType(const VariantType& value);
extern FLAXENGINE_API Variant UnboxVariant(MObject* value); extern FLAXENGINE_API Variant UnboxVariant(MObject* value, bool releaseHandle = false);
extern FLAXENGINE_API MObject* BoxVariant(const Variant& value); extern FLAXENGINE_API MObject* BoxVariant(const Variant& value);
} }
@@ -49,10 +49,58 @@ struct MConverter
{ {
MObject* Box(const T& data, const MClass* klass); MObject* Box(const T& data, const MClass* klass);
void Unbox(T& result, MObject* data); void Unbox(T& result, MObject* data);
void FreeManaged(MObject* data);
void ToManagedArray(MArray* result, const Span<T>& data); void ToManagedArray(MArray* result, const Span<T>& data);
void ToNativeArray(Span<T>& result, const MArray* data); void ToNativeArray(Span<T>& result, const MArray* data);
void FreeManagedArray(MArray* array);
}; };
// Simple array used to pass arrays of blittable data between managed and unmanaged side.
template<typename T>
struct NativeArray
{
T* data;
int32 length;
};
#if USE_NETCORE
// Special case for boolean values, the handles are fixed and should not be removed
template<>
struct MConverter<bool>
{
MObject* Box(const bool& data, const MClass* klass)
{
return MCore::Object::Box((void*)&data, klass);
}
void Unbox(bool& result, MObject* data)
{
if (data)
MCore::Object::Unbox(data, &result);
}
void FreeManaged(MObject* data)
{
}
void ToManagedArray(MArray* result, const Span<bool>& data)
{
Platform::MemoryCopy(MCore::Array::GetAddress(result), data.Get(), data.Length() * sizeof(bool));
}
void ToNativeArray(Span<bool>& result, const MArray* data)
{
Platform::MemoryCopy(result.Get(), MCore::Array::GetAddress(data), result.Length() * sizeof(bool));
}
void FreeManagedArray(MArray* array)
{
MCore::Array::Free(array);
MCore::GCHandle::Free(*(MGCHandle*)&array);
}
};
#endif
// Converter for POD types (that can use raw memory copy). // Converter for POD types (that can use raw memory copy).
template<typename T> template<typename T>
struct MConverter<T, typename TEnableIf<TAnd<TIsPODType<T>, TNot<TIsBaseOf<class ScriptingObject, typename TRemovePointer<T>::Type>>>::Value>::Type> struct MConverter<T, typename TEnableIf<TAnd<TIsPODType<T>, TNot<TIsBaseOf<class ScriptingObject, typename TRemovePointer<T>::Type>>>::Value>::Type>
@@ -65,7 +113,7 @@ struct MConverter<T, typename TEnableIf<TAnd<TIsPODType<T>, TNot<TIsBaseOf<class
void Unbox(T& result, MObject* data) void Unbox(T& result, MObject* data)
{ {
if (data) if (data)
Platform::MemoryCopy(&result, MCore::Object::Unbox(data), sizeof(T)); MCore::Object::Unbox(data, &result);
} }
void ToManagedArray(MArray* result, const Span<T>& data) void ToManagedArray(MArray* result, const Span<T>& data)
@@ -77,6 +125,17 @@ struct MConverter<T, typename TEnableIf<TAnd<TIsPODType<T>, TNot<TIsBaseOf<class
{ {
Platform::MemoryCopy(result.Get(), MCore::Array::GetAddress(data), result.Length() * sizeof(T)); Platform::MemoryCopy(result.Get(), MCore::Array::GetAddress(data), result.Length() * sizeof(T));
} }
void FreeManaged(MObject* data)
{
MCore::GCHandle::Free(*(MGCHandle*)&data);
}
void FreeManagedArray(MArray* array)
{
MCore::Array::Free(array);
MCore::GCHandle::Free(*(MGCHandle*)&array);
}
}; };
// Converter for String. // Converter for String.
@@ -85,22 +144,22 @@ struct MConverter<String>
{ {
MObject* Box(const String& data, const MClass* klass) MObject* Box(const String& data, const MClass* klass)
{ {
#if USE_NETCORE /*#if USE_NETCORE
MString* str = MUtils::ToString(data); MString* str = MUtils::ToString(data);
return MCore::Object::Box(str, klass); return MCore::Object::Box(str, klass);
#else #else*/
return (MObject*)MUtils::ToString(data); return (MObject*)MUtils::ToString(data);
#endif //#endif
} }
void Unbox(String& result, MObject* data) void Unbox(String& result, MObject* data)
{ {
#if USE_NETCORE /*#if USE_NETCORE
MString* str = (MString*)MCore::Object::Unbox(data); MString* str = (MString*)MCore::Object::Unbox(data);
result = MUtils::ToString(str); result = MUtils::ToString(str);
#else #else*/
result = MUtils::ToString((MString*)data); result = MUtils::ToString((MString*)data);
#endif //#endif
} }
void ToManagedArray(MArray* result, const Span<String>& data) void ToManagedArray(MArray* result, const Span<String>& data)
@@ -120,6 +179,23 @@ struct MConverter<String>
for (int32 i = 0; i < result.Length(); i++) for (int32 i = 0; i < result.Length(); i++)
MUtils::ToString(dataPtr[i], result.Get()[i]); MUtils::ToString(dataPtr[i], result.Get()[i]);
} }
void FreeManaged(MObject* data)
{
//MString* str = (MString*)MCore::Object::Unbox(data);
//MCore::GCHandle::Free(*(MGCHandle*)&str);
MCore::String::Free((MString*)data);
}
void FreeManagedArray(MArray* array)
{
MString** dataPtr = MCore::Array::GetAddress<MString*>(array);
auto length = MCore::Array::GetLength(array);
for (int32 i = 0; i < length; i++)
MCore::String::Free(dataPtr[i]);
MCore::Array::Free(array);
MCore::GCHandle::Free(*(MGCHandle*)&array);
}
}; };
// Converter for StringAnsi. // Converter for StringAnsi.
@@ -153,6 +229,21 @@ struct MConverter<StringAnsi>
for (int32 i = 0; i < result.Length(); i++) for (int32 i = 0; i < result.Length(); i++)
MUtils::ToString(dataPtr[i], result.Get()[i]); MUtils::ToString(dataPtr[i], result.Get()[i]);
} }
void FreeManaged(MObject* data)
{
MCore::String::Free((MString*)data);
}
void FreeManagedArray(MArray* array)
{
MString** dataPtr = MCore::Array::GetAddress<MString*>(array);
auto length = MCore::Array::GetLength(array);
for (int32 i = 0; i < length; i++)
MCore::String::Free(dataPtr[i]);
MCore::Array::Free(array);
MCore::GCHandle::Free(*(MGCHandle*)&array);
}
}; };
// Converter for StringView. // Converter for StringView.
@@ -161,16 +252,19 @@ struct MConverter<StringView>
{ {
MObject* Box(const StringView& data, const MClass* klass) MObject* Box(const StringView& data, const MClass* klass)
{ {
PLATFORM_DEBUG_BREAK; // FIXME
return (MObject*)MUtils::ToString(data); return (MObject*)MUtils::ToString(data);
} }
void Unbox(StringView& result, MObject* data) void Unbox(StringView& result, MObject* data)
{ {
PLATFORM_DEBUG_BREAK; // FIXME
result = MUtils::ToString((MString*)data); result = MUtils::ToString((MString*)data);
} }
void ToManagedArray(MArray* result, const Span<StringView>& data) void ToManagedArray(MArray* result, const Span<StringView>& data)
{ {
PLATFORM_DEBUG_BREAK; // FIXME
if (data.Length() == 0) if (data.Length() == 0)
return; return;
MObject** objects = (MObject**)Allocator::Allocate(data.Length() * sizeof(MObject*)); MObject** objects = (MObject**)Allocator::Allocate(data.Length() * sizeof(MObject*));
@@ -182,10 +276,28 @@ struct MConverter<StringView>
void ToNativeArray(Span<StringView>& result, const MArray* data) void ToNativeArray(Span<StringView>& result, const MArray* data)
{ {
PLATFORM_DEBUG_BREAK; // FIXME
MString** dataPtr = MCore::Array::GetAddress<MString*>(data); MString** dataPtr = MCore::Array::GetAddress<MString*>(data);
for (int32 i = 0; i < result.Length(); i++) for (int32 i = 0; i < result.Length(); i++)
MUtils::ToString(dataPtr[i], result.Get()[i]); MUtils::ToString(dataPtr[i], result.Get()[i]);
} }
void FreeManaged(MObject* data)
{
PLATFORM_DEBUG_BREAK; // FIXME
MCore::String::Free((MString*)data);
}
void FreeManagedArray(MArray* array)
{
PLATFORM_DEBUG_BREAK; // FIXME
MString** dataPtr = MCore::Array::GetAddress<MString*>(array);
auto length = MCore::Array::GetLength(array);
for (int32 i = 0; i < length; i++)
MCore::String::Free(dataPtr[i]);
MCore::Array::Free(array);
MCore::GCHandle::Free(*(MGCHandle*)&array);
}
}; };
// Converter for Variant. // Converter for Variant.
@@ -194,16 +306,19 @@ struct MConverter<Variant>
{ {
MObject* Box(const Variant& data, const MClass* klass) MObject* Box(const Variant& data, const MClass* klass)
{ {
PLATFORM_DEBUG_BREAK; // FIXME
return MUtils::BoxVariant(data); return MUtils::BoxVariant(data);
} }
void Unbox(Variant& result, MObject* data) void Unbox(Variant& result, MObject* data)
{ {
PLATFORM_DEBUG_BREAK; // FIXME
result = MUtils::UnboxVariant(data); result = MUtils::UnboxVariant(data);
} }
void ToManagedArray(MArray* result, const Span<Variant>& data) void ToManagedArray(MArray* result, const Span<Variant>& data)
{ {
PLATFORM_DEBUG_BREAK; // FIXME
if (data.Length() == 0) if (data.Length() == 0)
return; return;
MObject** objects = (MObject**)Allocator::Allocate(data.Length() * sizeof(MObject*)); MObject** objects = (MObject**)Allocator::Allocate(data.Length() * sizeof(MObject*));
@@ -215,10 +330,23 @@ struct MConverter<Variant>
void ToNativeArray(Span<Variant>& result, const MArray* data) void ToNativeArray(Span<Variant>& result, const MArray* data)
{ {
PLATFORM_DEBUG_BREAK; // FIXME
MObject** dataPtr = MCore::Array::GetAddress<MObject*>(data); MObject** dataPtr = MCore::Array::GetAddress<MObject*>(data);
for (int32 i = 0; i < result.Length(); i++) for (int32 i = 0; i < result.Length(); i++)
result.Get()[i] = MUtils::UnboxVariant(dataPtr[i]); result.Get()[i] = MUtils::UnboxVariant(dataPtr[i]);
} }
void FreeManaged(MObject* data)
{
PLATFORM_DEBUG_BREAK; // FIXME
}
void FreeManagedArray(MArray* array)
{
PLATFORM_DEBUG_BREAK; // FIXME
MCore::Array::Free(array);
MCore::GCHandle::Free(*(MGCHandle*)&array);
}
}; };
// Converter for Scripting Objects (collection of pointers). // Converter for Scripting Objects (collection of pointers).
@@ -227,11 +355,13 @@ struct MConverter<T*, typename TEnableIf<TIsBaseOf<class ScriptingObject, T>::Va
{ {
MObject* Box(T* data, const MClass* klass) MObject* Box(T* data, const MClass* klass)
{ {
PLATFORM_DEBUG_BREAK; // FIXME
return data ? data->GetOrCreateManagedInstance() : nullptr; return data ? data->GetOrCreateManagedInstance() : nullptr;
} }
void Unbox(T*& result, MObject* data) void Unbox(T*& result, MObject* data)
{ {
PLATFORM_DEBUG_BREAK; // FIXME
result = (T*)ScriptingObject::ToNative(data); result = (T*)ScriptingObject::ToNative(data);
} }
@@ -252,6 +382,17 @@ struct MConverter<T*, typename TEnableIf<TIsBaseOf<class ScriptingObject, T>::Va
for (int32 i = 0; i < result.Length(); i++) for (int32 i = 0; i < result.Length(); i++)
result.Get()[i] = (T*)ScriptingObject::ToNative(dataPtr[i]); result.Get()[i] = (T*)ScriptingObject::ToNative(dataPtr[i]);
} }
void FreeManaged(MObject* data)
{
PLATFORM_DEBUG_BREAK; // FIXME
}
void FreeManagedArray(MArray* array)
{
MCore::Array::Free(array);
MCore::GCHandle::Free(*(MGCHandle*)&array);
}
}; };
// Converter for Scripting Objects (collection of values). // Converter for Scripting Objects (collection of values).
@@ -260,11 +401,13 @@ struct MConverter<T, typename TEnableIf<TIsBaseOf<class ScriptingObject, T>::Val
{ {
MObject* Box(const T& data, const MClass* klass) MObject* Box(const T& data, const MClass* klass)
{ {
PLATFORM_DEBUG_BREAK; // FIXME
return data.GetOrCreateManagedInstance(); return data.GetOrCreateManagedInstance();
} }
void ToManagedArray(MArray* result, const Span<T>& data) void ToManagedArray(MArray* result, const Span<T>& data)
{ {
PLATFORM_DEBUG_BREAK; // FIXME
if (data.Length() == 0) if (data.Length() == 0)
return; return;
MObject** objects = (MObject**)Allocator::Allocate(data.Length() * sizeof(MObject*)); MObject** objects = (MObject**)Allocator::Allocate(data.Length() * sizeof(MObject*));
@@ -273,6 +416,18 @@ struct MConverter<T, typename TEnableIf<TIsBaseOf<class ScriptingObject, T>::Val
MCore::GC::WriteArrayRef(result, Span<MObject*>(objects, data.Length())); MCore::GC::WriteArrayRef(result, Span<MObject*>(objects, data.Length()));
Allocator::Free(objects); Allocator::Free(objects);
} }
void FreeManaged(MObject* data)
{
PLATFORM_DEBUG_BREAK; // FIXME
}
void FreeManagedArray(MArray* array)
{
PLATFORM_DEBUG_BREAK; // FIXME
MCore::Array::Free(array);
MCore::GCHandle::Free(*(MGCHandle*)&array);
}
}; };
// Converter for ScriptingObject References. // Converter for ScriptingObject References.
@@ -284,16 +439,19 @@ struct MConverter<ScriptingObjectReference<T>>
{ {
MObject* Box(const ScriptingObjectReference<T>& data, const MClass* klass) MObject* Box(const ScriptingObjectReference<T>& data, const MClass* klass)
{ {
PLATFORM_DEBUG_BREAK; // FIXME
return data.GetManagedInstance(); return data.GetManagedInstance();
} }
void Unbox(ScriptingObjectReference<T>& result, MObject* data) void Unbox(ScriptingObjectReference<T>& result, MObject* data)
{ {
PLATFORM_DEBUG_BREAK; // FIXME
result = (T*)ScriptingObject::ToNative(data); result = (T*)ScriptingObject::ToNative(data);
} }
void ToManagedArray(MArray* result, const Span<ScriptingObjectReference<T>>& data) void ToManagedArray(MArray* result, const Span<ScriptingObjectReference<T>>& data)
{ {
PLATFORM_DEBUG_BREAK; // FIXME
if (data.Length() == 0) if (data.Length() == 0)
return; return;
MObject** objects = (MObject**)Allocator::Allocate(data.Length() * sizeof(MObject*)); MObject** objects = (MObject**)Allocator::Allocate(data.Length() * sizeof(MObject*));
@@ -305,10 +463,23 @@ struct MConverter<ScriptingObjectReference<T>>
void ToNativeArray(Span<ScriptingObjectReference<T>>& result, const MArray* data) void ToNativeArray(Span<ScriptingObjectReference<T>>& result, const MArray* data)
{ {
PLATFORM_DEBUG_BREAK; // FIXME
MObject** dataPtr = MCore::Array::GetAddress<MObject*>(data); MObject** dataPtr = MCore::Array::GetAddress<MObject*>(data);
for (int32 i = 0; i < result.Length(); i++) for (int32 i = 0; i < result.Length(); i++)
result.Get()[i] = (T*)ScriptingObject::ToNative(dataPtr[i]); result.Get()[i] = (T*)ScriptingObject::ToNative(dataPtr[i]);
} }
void FreeManaged(MObject* data)
{
PLATFORM_DEBUG_BREAK; // FIXME
}
void FreeManagedArray(MArray* array)
{
PLATFORM_DEBUG_BREAK; // FIXME
MCore::Array::Free(array);
MCore::GCHandle::Free(*(MGCHandle*)&array);
}
}; };
// Converter for Asset References. // Converter for Asset References.
@@ -320,11 +491,13 @@ struct MConverter<AssetReference<T>>
{ {
MObject* Box(const AssetReference<T>& data, const MClass* klass) MObject* Box(const AssetReference<T>& data, const MClass* klass)
{ {
PLATFORM_DEBUG_BREAK; // FIXME
return data.GetManagedInstance(); return data.GetManagedInstance();
} }
void Unbox(AssetReference<T>& result, MObject* data) void Unbox(AssetReference<T>& result, MObject* data)
{ {
PLATFORM_DEBUG_BREAK; // FIXME
result = (T*)ScriptingObject::ToNative(data); result = (T*)ScriptingObject::ToNative(data);
} }
@@ -341,10 +514,22 @@ struct MConverter<AssetReference<T>>
void ToNativeArray(Span<AssetReference<T>>& result, const MArray* data) void ToNativeArray(Span<AssetReference<T>>& result, const MArray* data)
{ {
//PLATFORM_DEBUG_BREAK; // FIXME
MObject** dataPtr = MCore::Array::GetAddress<MObject*>(data); MObject** dataPtr = MCore::Array::GetAddress<MObject*>(data);
for (int32 i = 0; i < result.Length(); i++) for (int32 i = 0; i < result.Length(); i++)
result.Get()[i] = (T*)ScriptingObject::ToNative(dataPtr[i]); result.Get()[i] = (T*)ScriptingObject::ToNative(dataPtr[i]);
} }
void FreeManaged(MObject* data)
{
PLATFORM_DEBUG_BREAK; // FIXME
}
void FreeManagedArray(MArray* array)
{
MCore::Array::Free(array);
MCore::GCHandle::Free(*(MGCHandle*)&array);
}
}; };
// TODO: use MarshalAs=Guid on SoftAssetReference to pass guid over bindings and not load asset in glue code // TODO: use MarshalAs=Guid on SoftAssetReference to pass guid over bindings and not load asset in glue code
@@ -355,6 +540,7 @@ struct MConverter<SoftAssetReference<T>>
{ {
void ToManagedArray(MArray* result, const Span<SoftAssetReference<T>>& data) void ToManagedArray(MArray* result, const Span<SoftAssetReference<T>>& data)
{ {
//PLATFORM_DEBUG_BREAK; // FIXME
if (data.Length() == 0) if (data.Length() == 0)
return; return;
MObject** objects = (MObject**)Allocator::Allocate(data.Length() * sizeof(MObject*)); MObject** objects = (MObject**)Allocator::Allocate(data.Length() * sizeof(MObject*));
@@ -366,10 +552,23 @@ struct MConverter<SoftAssetReference<T>>
void ToNativeArray(Span<SoftAssetReference<T>>& result, const MArray* data) void ToNativeArray(Span<SoftAssetReference<T>>& result, const MArray* data)
{ {
//PLATFORM_DEBUG_BREAK; // FIXME
MObject** dataPtr = MCore::Array::GetAddress<MObject*>(data); MObject** dataPtr = MCore::Array::GetAddress<MObject*>(data);
for (int32 i = 0; i < result.Length(); i++) for (int32 i = 0; i < result.Length(); i++)
result.Get()[i] = (T*)ScriptingObject::ToNative(dataPtr[i]); result.Get()[i] = (T*)ScriptingObject::ToNative(dataPtr[i]);
} }
void FreeManaged(MObject* data)
{
PLATFORM_DEBUG_BREAK; // FIXME
}
void FreeManagedArray(MArray* array)
{
PLATFORM_DEBUG_BREAK; // FIXME
MCore::Array::Free(array);
MCore::GCHandle::Free(*(MGCHandle*)&array);
}
}; };
// Converter for Array. // Converter for Array.
@@ -378,6 +577,7 @@ struct MConverter<Array<T>>
{ {
MObject* Box(const Array<T>& data, const MClass* klass) MObject* Box(const Array<T>& data, const MClass* klass)
{ {
PLATFORM_DEBUG_BREAK; // FIXME
if (!klass) if (!klass)
return nullptr; return nullptr;
MArray* result = MCore::Array::New(klass->GetElementClass(), data.Count()); MArray* result = MCore::Array::New(klass->GetElementClass(), data.Count());
@@ -395,6 +595,23 @@ struct MConverter<Array<T>>
Span<T> resultSpan(result.Get(), length); Span<T> resultSpan(result.Get(), length);
converter.ToNativeArray(resultSpan, array); converter.ToNativeArray(resultSpan, array);
} }
void FreeManaged(MObject* data)
{
PLATFORM_DEBUG_BREAK; // FIXME
MArray* array = (MArray*)data;
MConverter<T> converter;
converter.FreeManagedArray(array);
//MCore::Array::Free(array);
//MCore::GCHandle::Free(*(MGCHandle*)&data);
}
void FreeManagedArray(MArray* array)
{
PLATFORM_DEBUG_BREAK; // FIXME
MCore::Array::Free(array);
MCore::GCHandle::Free(*(MGCHandle*)&array);
}
}; };
namespace MUtils namespace MUtils
@@ -435,6 +652,7 @@ namespace MUtils
/// <summary> /// <summary>
/// Unboxes MObject to the native value of the given type. /// Unboxes MObject to the native value of the given type.
/// </summary> /// </summary>
///
template<class T> template<class T>
T Unbox(MObject* object) T Unbox(MObject* object)
{ {
@@ -444,6 +662,71 @@ namespace MUtils
return result; return result;
} }
/// <summary>
/// Unboxes MObject to the native value of the given type.
/// </summary>
/// <param name="object">The object.</param>
/// <param name="releaseHandle">True if boxed managed handle should be released.</param>
template<class T>
T Unbox(MObject* object, bool releaseHandle)
{
MConverter<T> converter;
T result;
converter.Unbox(result, object);
if (releaseHandle)
converter.FreeManaged(object);
return result;
}
/// <summary>
/// Unboxes MObject to the native value of the given type.
/// </summary>
///
template<>
inline bool Unbox(MObject* object)
{
MConverter<bool> converter;
int32 result;
converter.Unbox(*(bool*)&result, object);
return result != 0;
}
/// <summary>
/// Unboxes MObject to the native value of the given type.
/// </summary>
/// <param name="object">The object.</param>
/// <param name="releaseHandle">True if boxed managed handle should be released.</param>
template<>
inline bool Unbox(MObject* object, bool releaseHandle)
{
MConverter<bool> converter;
int32 result;
converter.Unbox(*(bool*)&result, object);
if (releaseHandle)
converter.FreeManaged(object);
return result != 0;
}
/// <summary>
/// Releases the managed resources of a boxed object.
/// </summary>
template<class T>
void FreeManaged(MObject* object)
{
MConverter<T> converter;
converter.FreeManaged(object);
}
/// <summary>
/// Releases the managed resources of a boxed object.
/// </summary>
template<class T>
void FreeManagedArray(MArray* array)
{
MConverter<T> converter;
converter.FreeManagedArray(array);
}
/// <summary> /// <summary>
/// Links managed array data to the unmanaged BytesContainer. /// Links managed array data to the unmanaged BytesContainer.
/// </summary> /// </summary>
@@ -580,6 +863,52 @@ namespace MUtils
} }
#if USE_NETCORE #if USE_NETCORE
/// <summary>
/// Allocates new boolean array and copies data from the given unmanaged data container. The managed runtime is responsible for releasing the returned array data.
/// </summary>
/// <param name="data">The input data.</param>
/// <returns>The output array.</returns>
template<typename T>
FORCE_INLINE T* ToNativeArray(const Array<T>& data)
{
// System.Runtime.InteropServices.Marshalling.ArrayMarshaller uses CoTask memory alloc to native data pointer
T* arr = (T*)MCore::GC::AllocateMemory(data.Count() * sizeof(T), true);
Platform::MemoryCopy(arr, data.Get(), data.Count() * sizeof(T));
return arr;
}
/// <summary>
/// Allocates new boolean array and copies data from the given unmanaged data container. The managed runtime is responsible for releasing the returned array data.
/// </summary>
/// <param name="data">The input data.</param>
/// <returns>The output array.</returns>
template<typename T, typename AllocationType = HeapAllocation>
FORCE_INLINE NativeArray<T> ToNativeArrayWrapper(const Array<T, AllocationType>& data)
{
// System.Runtime.InteropServices.Marshalling.ArrayMarshaller uses CoTask memory alloc to native data pointer
NativeArray<T> arr;
arr.length = data.Count();
arr.data = (T*)MCore::GC::AllocateMemory(arr.length * sizeof(T), true);
Platform::MemoryCopy(arr.data, data.Get(), arr.length * sizeof(T));
return arr;
}
/// <summary>
/// Allocates new boolean array and copies data from the given unmanaged data container. The managed runtime is responsible for releasing the returned array data.
/// </summary>
/// <param name="data">The input data.</param>
/// <returns>The output array.</returns>
template<typename T>
FORCE_INLINE NativeArray<T> ToNativeArrayWrapper(const Span<T>& data)
{
// System.Runtime.InteropServices.Marshalling.ArrayMarshaller uses CoTask memory alloc to native data pointer
NativeArray<T> arr;
arr.length = data.Length();
arr.data = (T*)MCore::GC::AllocateMemory(arr.length * sizeof(T), true);
Platform::MemoryCopy(arr.data, data.Get(), arr.length * sizeof(T));
return arr;
}
/// <summary> /// <summary>
/// Allocates new boolean array and copies data from the given unmanaged data container. The managed runtime is responsible for releasing the returned array data. /// Allocates new boolean array and copies data from the given unmanaged data container. The managed runtime is responsible for releasing the returned array data.
/// </summary> /// </summary>

View File

@@ -400,9 +400,14 @@ MObject* MCore::Object::Box(void* value, const MClass* klass)
} }
void* MCore::Object::Unbox(MObject* obj) void* MCore::Object::Unbox(MObject* obj)
{
CRASH; // Should not be used anymore
}
void MCore::Object::Unbox(MObject* obj, void* dest)
{ {
static void* UnboxValuePtr = GetStaticMethodPointer(TEXT("UnboxValue")); static void* UnboxValuePtr = GetStaticMethodPointer(TEXT("UnboxValue"));
return CallStaticMethod<void*, void*>(UnboxValuePtr, obj); return CallStaticMethod<void, void*, void*>(UnboxValuePtr, obj, dest);
} }
MObject* MCore::Object::New(const MClass* klass) MObject* MCore::Object::New(const MClass* klass)
@@ -462,12 +467,24 @@ StringView MCore::String::GetChars(MString* obj)
return StringView(chars, length); return StringView(chars, length);
} }
void MCore::String::Free(MString* obj)
{
static void* FreeStringPtr = GetStaticMethodPointer(TEXT("FreeString"));
CallStaticMethod<void, void*>(FreeStringPtr, obj);
}
MArray* MCore::Array::New(const MClass* elementKlass, int32 length) MArray* MCore::Array::New(const MClass* elementKlass, int32 length)
{ {
static void* NewArrayPtr = GetStaticMethodPointer(TEXT("NewArray")); static void* NewArrayPtr = GetStaticMethodPointer(TEXT("NewArray"));
return (MArray*)CallStaticMethod<void*, void*, long long>(NewArrayPtr, elementKlass->_handle, length); return (MArray*)CallStaticMethod<void*, void*, long long>(NewArrayPtr, elementKlass->_handle, length);
} }
void MCore::Array::Free(const MArray* array)
{
static void* FreeArrayPtr = GetStaticMethodPointer(TEXT("FreeArray"));
CallStaticMethod<void, void*>(FreeArrayPtr, (void*)array);
}
MClass* MCore::Array::GetClass(MClass* elementKlass) MClass* MCore::Array::GetClass(MClass* elementKlass)
{ {
static void* GetArrayTypeFromElementTypePtr = GetStaticMethodPointer(TEXT("GetArrayTypeFromElementType")); static void* GetArrayTypeFromElementTypePtr = GetStaticMethodPointer(TEXT("GetArrayTypeFromElementType"));
@@ -883,7 +900,6 @@ bool MAssembly::LoadCorlib()
bool MAssembly::LoadImage(const String& assemblyPath, const StringView& nativePath) bool MAssembly::LoadImage(const String& assemblyPath, const StringView& nativePath)
{ {
// TODO: Use new hostfxr delegate load_assembly_bytes? (.NET 8+)
// Open .Net assembly // Open .Net assembly
static void* LoadAssemblyImagePtr = GetStaticMethodPointer(TEXT("LoadAssemblyImage")); static void* LoadAssemblyImagePtr = GetStaticMethodPointer(TEXT("LoadAssemblyImage"));
_handle = CallStaticMethod<void*, const Char*>(LoadAssemblyImagePtr, assemblyPath.Get()); _handle = CallStaticMethod<void*, const Char*>(LoadAssemblyImagePtr, assemblyPath.Get());

View File

@@ -738,6 +738,11 @@ void* MCore::Object::Unbox(MObject* obj)
//return mono_object_unbox(obj); //return mono_object_unbox(obj);
} }
void MCore::Object::Unbox(MObject* obj, void* dest)
{
CRASH; // Not applicable
}
MObject* MCore::Object::New(const MClass* klass) MObject* MCore::Object::New(const MClass* klass)
{ {
return mono_object_new(mono_domain_get(), klass->GetNative()); return mono_object_new(mono_domain_get(), klass->GetNative());
@@ -787,12 +792,20 @@ StringView MCore::String::GetChars(MString* obj)
return StringView(mono_string_chars(obj), (int32)mono_string_length(obj)); return StringView(mono_string_chars(obj), (int32)mono_string_length(obj));
} }
void MCore::String::Free(MString* obj)
{
}
MArray* MCore::Array::New(const MClass* elementKlass, int32 length) MArray* MCore::Array::New(const MClass* elementKlass, int32 length)
{ {
// TODO: use shared empty arrays cache // TODO: use shared empty arrays cache
return mono_array_new(mono_domain_get(), elementKlass->GetNative(), length); return mono_array_new(mono_domain_get(), elementKlass->GetNative(), length);
} }
void MCore::Array::Free(const MArray* array)
{
}
MClass* MCore::Array::GetClass(MClass* elementKlass) MClass* MCore::Array::GetClass(MClass* elementKlass)
{ {
MonoClass* monoClass = mono_array_class_get(elementKlass->GetNative(), 1); MonoClass* monoClass = mono_array_class_get(elementKlass->GetNative(), 1);

View File

@@ -80,6 +80,10 @@ void* MCore::Object::Unbox(MObject* obj)
return nullptr; return nullptr;
} }
void MCore::Object::Unbox(MObject* obj, void* dest)
{
}
MObject* MCore::Object::New(const MClass* klass) MObject* MCore::Object::New(const MClass* klass)
{ {
return nullptr; return nullptr;
@@ -124,11 +128,19 @@ StringView MCore::String::GetChars(MString* obj)
return StringView::Empty; return StringView::Empty;
} }
void MCore::String::Free(MString* obj)
{
}
MArray* MCore::Array::New(const MClass* elementKlass, int32 length) MArray* MCore::Array::New(const MClass* elementKlass, int32 length)
{ {
return nullptr; return nullptr;
} }
void MCore::Array::Free(const MArray* array)
{
}
MClass* MCore::Array::GetClass(MClass* elementKlass) MClass* MCore::Array::GetClass(MClass* elementKlass)
{ {
return nullptr; return nullptr;

View File

@@ -0,0 +1,82 @@
// Copyright (c) Wojciech Figat. All rights reserved.
#include "Engine/Engine/CommandLine.h"
#include "Engine/Core/Types/StringView.h"
#include "Engine/Core/Collections/Array.h"
#include <ThirdParty/catch2/catch.hpp>
TEST_CASE("CommandLine")
{
SECTION("Test Argument Parser")
{
SECTION("Single quoted word")
{
String input("\"word\"");
Array<StringAnsi> arguments;
CHECK(!CommandLine::ParseArguments(input, arguments));
CHECK(arguments.Count() == 1);
CHECK(arguments[0].Compare(StringAnsi("word")) == 0);
}
SECTION("Quotes at the beginning of the word")
{
String input("start\"word\"");
Array<StringAnsi> arguments;
CHECK(!CommandLine::ParseArguments(input, arguments));
CHECK(arguments.Count() == 1);
CHECK(arguments[0].Compare(StringAnsi("start\"word\"")) == 0);
}
SECTION("Quotes in the middle of the word")
{
String input("start\"word\"end");
Array<StringAnsi> arguments;
CHECK(!CommandLine::ParseArguments(input, arguments));
CHECK(arguments.Count() == 1);
CHECK(arguments[0].Compare(StringAnsi("start\"word\"end")) == 0);
}
SECTION("Quotes at the end of the word")
{
String input("\"word\"end");
Array<StringAnsi> arguments;
CHECK(!CommandLine::ParseArguments(input, arguments));
CHECK(arguments.Count() == 1);
CHECK(arguments[0].Compare(StringAnsi("\"word\"end")) == 0);
}
SECTION("Multiple words")
{
String input("The quick brown fox");
Array<StringAnsi> arguments;
CHECK(!CommandLine::ParseArguments(input, arguments));
CHECK(arguments.Count() == 4);
CHECK(arguments[0].Compare(StringAnsi("The")) == 0);
CHECK(arguments[1].Compare(StringAnsi("quick")) == 0);
CHECK(arguments[2].Compare(StringAnsi("brown")) == 0);
CHECK(arguments[3].Compare(StringAnsi("fox")) == 0);
}
SECTION("Multiple words with quotes")
{
String input("The \"quick brown fox\" jumps over the \"lazy\" dog");
Array<StringAnsi> arguments;
CHECK(!CommandLine::ParseArguments(input, arguments));
CHECK(arguments.Count() == 7);
CHECK(arguments[0].Compare(StringAnsi("The")) == 0);
CHECK(arguments[1].Compare(StringAnsi("quick brown fox")) == 0);
CHECK(arguments[2].Compare(StringAnsi("jumps")) == 0);
CHECK(arguments[3].Compare(StringAnsi("over")) == 0);
CHECK(arguments[4].Compare(StringAnsi("the")) == 0);
CHECK(arguments[5].Compare(StringAnsi("lazy")) == 0);
CHECK(arguments[6].Compare(StringAnsi("dog")) == 0);
}
SECTION("Flax.Build sample parameters")
{
String input("-log -mutex -workspace=\"C:\\path with spaces/to/FlaxEngine/\" -configuration=Debug -hotreload=\".HotReload.1\"");
Array<StringAnsi> arguments;
CHECK(!CommandLine::ParseArguments(input, arguments));
CHECK(arguments.Count() == 5);
CHECK(arguments[0].Compare(StringAnsi("-log")) == 0);
CHECK(arguments[1].Compare(StringAnsi("-mutex")) == 0);
CHECK(arguments[2].Compare(StringAnsi("-workspace=\"C:\\path with spaces/to/FlaxEngine/\"")) == 0);
CHECK(arguments[3].Compare(StringAnsi("-configuration=Debug")) == 0);
CHECK(arguments[4].Compare(StringAnsi("-hotreload=\".HotReload.1\"")) == 0);
}
}
}

View File

@@ -38,7 +38,7 @@ TEST_CASE("Scripting")
CHECK(method); CHECK(method);
MObject* result = method->Invoke(nullptr, nullptr, nullptr); MObject* result = method->Invoke(nullptr, nullptr, nullptr);
CHECK(result); CHECK(result);
int32 resultValue = MUtils::Unbox<int32>(result); int32 resultValue = MUtils::Unbox<int32>(result, true);
CHECK(resultValue == 0); CHECK(resultValue == 0);
} }

View File

@@ -745,7 +745,7 @@ void VisjectExecutor::ProcessGroupPacking(Box* box, Node* node, Value& value)
StringAsANSI<40> fieldNameAnsi(*fieldName, fieldName.Length()); StringAsANSI<40> fieldNameAnsi(*fieldName, fieldName.Length());
auto field = mclass->GetField(fieldNameAnsi.Get()); auto field = mclass->GetField(fieldNameAnsi.Get());
if (field) if (field)
value = MUtils::UnboxVariant(field->GetValueBoxed(instance)); value = MUtils::UnboxVariant(field->GetValueBoxed(instance), true);
break; break;
} }
} }

View File

@@ -633,8 +633,62 @@ namespace Flax.Build.Bindings
returnMarshalType = "MarshalUsing(typeof(FlaxEngine.Interop.SystemArrayMarshaller))"; returnMarshalType = "MarshalUsing(typeof(FlaxEngine.Interop.SystemArrayMarshaller))";
else if (returnValueType == "object[]") else if (returnValueType == "object[]")
returnMarshalType = "MarshalUsing(typeof(FlaxEngine.Interop.SystemObjectArrayMarshaller))"; returnMarshalType = "MarshalUsing(typeof(FlaxEngine.Interop.SystemObjectArrayMarshaller))";
else if (functionInfo.ReturnType.IsArrayOrSpan || returnNativeType == "Array")
{
string unmanagedType = "";
if (functionInfo.ReturnType.GenericArgs != null && functionInfo.ReturnType.GenericArgs[0].IsPod(buildData, caller))
{
switch (functionInfo.ReturnType.GenericArgs[0].Type)
{
case "bool": unmanagedType = "U1"; break;
case "byte": unmanagedType = "U1"; break;
case "sbyte": unmanagedType = "I1"; break;
case "char": unmanagedType = "I2"; break;
case "short": case "int16": unmanagedType = "I2"; break;
case "ushort": case "uint16": unmanagedType = "U2"; break;
case "int": case "int32": unmanagedType = "I4"; break;
case "uint": case "uint32": unmanagedType = "U4"; break;
case "long": case "int64": unmanagedType = "I8"; break;
case "ulong": case "uint64": unmanagedType = "U8"; break;
case "float": unmanagedType = "R4"; break;
case "double": unmanagedType = "R8"; break;
case "Float2":
case "Double2":
case "Vector2":
case "Float3":
case "Double3":
case "Vector3":
case "Float4":
case "Double4":
case "Vector4":
case "Color":
case "Color32":
case "Guid":
unmanagedType = "Any"; // FIXME
break;
default:
unmanagedType = "Any";
//Log.Warning($"unknown type: '{parameterInfo.Type.GenericArgs[0].Type}'");
break;
}
}
if (!string.IsNullOrEmpty(unmanagedType))
{
string arraySubType = "";
if (unmanagedType != "Any")
arraySubType = $"ArraySubType = UnmanagedType.{unmanagedType}, ";
returnMarshalType = $"MarshalAs(UnmanagedType.LPArray, {arraySubType}SizeParamIndex = {(!functionInfo.IsStatic ? 1 : 0) + functionInfo.Parameters.Count + (functionInfo.Glue.CustomParameters.FindIndex(x => x.Name == $"__returnCount"))})";
}
else
returnMarshalType = $"MarshalUsing(typeof(FlaxEngine.Interop.ArrayMarshaller<,>), CountElementName = \"__returnCount\")";
}
else if (functionInfo.ReturnType.Type == "Array" || functionInfo.ReturnType.Type == "Span" || functionInfo.ReturnType.Type == "DataContainer" || functionInfo.ReturnType.Type == "BytesContainer" || returnNativeType == "Array") else if (functionInfo.ReturnType.Type == "Array" || functionInfo.ReturnType.Type == "Span" || functionInfo.ReturnType.Type == "DataContainer" || functionInfo.ReturnType.Type == "BytesContainer" || returnNativeType == "Array")
{
//[MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U1, SizeParamIndex = 8)]
returnMarshalType = "MarshalUsing(typeof(FlaxEngine.Interop.ArrayMarshaller<,>), CountElementName = nameof(__returnCount))"; returnMarshalType = "MarshalUsing(typeof(FlaxEngine.Interop.ArrayMarshaller<,>), CountElementName = nameof(__returnCount))";
}
else if (functionInfo.ReturnType.Type == "Dictionary") else if (functionInfo.ReturnType.Type == "Dictionary")
returnMarshalType = "MarshalUsing(typeof(FlaxEngine.Interop.DictionaryMarshaller<,>), ConstantElementCount = 0)"; returnMarshalType = "MarshalUsing(typeof(FlaxEngine.Interop.DictionaryMarshaller<,>), ConstantElementCount = 0)";
else if (returnValueType == "byte[]") else if (returnValueType == "byte[]")
@@ -686,10 +740,55 @@ namespace Flax.Build.Bindings
parameterMarshalType = "MarshalUsing(typeof(FlaxEngine.Interop.SystemArrayMarshaller))"; parameterMarshalType = "MarshalUsing(typeof(FlaxEngine.Interop.SystemArrayMarshaller))";
else if (nativeType == "object[]") else if (nativeType == "object[]")
parameterMarshalType = "MarshalUsing(typeof(FlaxEngine.Interop.SystemObjectArrayMarshaller))"; parameterMarshalType = "MarshalUsing(typeof(FlaxEngine.Interop.SystemObjectArrayMarshaller))";
else if (parameterInfo.Type.Type == "Array" && parameterInfo.Type.GenericArgs.Count > 0 && parameterInfo.Type.GenericArgs[0].Type == "bool") else if (parameterInfo.Type.IsArrayOrSpan || nativeType == "Array")
parameterMarshalType = $"MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U1, SizeParamIndex = {(!functionInfo.IsStatic ? 1 : 0) + functionInfo.Parameters.Count + (functionInfo.Glue.CustomParameters.FindIndex(x => x.Name == $"__{parameterInfo.Name}Count"))})";
else if (parameterInfo.Type.Type == "Array" || parameterInfo.Type.Type == "Span" || parameterInfo.Type.Type == "DataContainer" || parameterInfo.Type.Type == "BytesContainer" || nativeType == "Array")
{ {
string unmanagedType = "";
if (parameterInfo.Type.GenericArgs != null && parameterInfo.Type.GenericArgs[0].IsPod(buildData, caller))
{
switch (parameterInfo.Type.GenericArgs[0].Type)
{
case "bool": unmanagedType = "U1"; break;
case "byte": unmanagedType = "U1"; break;
case "sbyte": unmanagedType = "I1"; break;
case "char": unmanagedType = "I2"; break;
case "short": case "int16": unmanagedType = "I2"; break;
case "ushort": case "uint16": unmanagedType = "U2"; break;
case "int": case "int32": unmanagedType = "I4"; break;
case "uint": case "uint32": unmanagedType = "U4"; break;
case "long": case "int64": unmanagedType = "I8"; break;
case "ulong": case "uint64": unmanagedType = "U8"; break;
case "float": unmanagedType = "R4"; break;
case "double": unmanagedType = "R8"; break;
case "Float2":
case "Double2":
case "Vector2":
case "Float3":
case "Double3":
case "Vector3":
case "Float4":
case "Double4":
case "Vector4":
case "Color":
case "Color32":
case "Guid":
unmanagedType = "Any"; // FIXME
break;
default:
unmanagedType = "Any";
//Log.Warning($"unknown type: '{parameterInfo.Type.GenericArgs[0].Type}'");
break;
}
}
if (!string.IsNullOrEmpty(unmanagedType))
{
string arraySubType = "";
if (unmanagedType != "Any")
arraySubType = $"ArraySubType = UnmanagedType.{unmanagedType}, ";
parameterMarshalType = $"MarshalAs(UnmanagedType.LPArray, {arraySubType}SizeParamIndex = {(!functionInfo.IsStatic ? 1 : 0) + functionInfo.Parameters.Count + (functionInfo.Glue.CustomParameters.FindIndex(x => x.Name == $"__{parameterInfo.Name}Count"))})";
}
else
parameterMarshalType = $"MarshalUsing(typeof(FlaxEngine.Interop.ArrayMarshaller<,>), CountElementName = \"__{parameterInfo.Name}Count\")"; parameterMarshalType = $"MarshalUsing(typeof(FlaxEngine.Interop.ArrayMarshaller<,>), CountElementName = \"__{parameterInfo.Name}Count\")";
if (!parameterInfo.IsOut && !parameterInfo.IsRef) if (!parameterInfo.IsOut && !parameterInfo.IsRef)
parameterMarshalType += ", In"; // The usage of 'LibraryImportAttribute' does not follow recommendations. It is recommended to use explicit '[In]' and '[Out]' attributes on array parameters. parameterMarshalType += ", In"; // The usage of 'LibraryImportAttribute' does not follow recommendations. It is recommended to use explicit '[In]' and '[Out]' attributes on array parameters.
@@ -711,7 +810,12 @@ namespace Flax.Build.Bindings
if (parameterInfo.IsOut) if (parameterInfo.IsOut)
contents.Append("out "); contents.Append("out ");
else if (parameterInfo.IsRef || UsePassByReference(buildData, parameterInfo.Type, caller)) else if (parameterInfo.IsRef || UsePassByReference(buildData, parameterInfo.Type, caller))
{
if (parameterInfo.IsConst || parameterInfo.Type.IsConst)
contents.Append("in ");
else
contents.Append("ref "); contents.Append("ref ");
}
// Out parameters that need additional converting will be converted at the native side (eg. object reference) // Out parameters that need additional converting will be converted at the native side (eg. object reference)
if (parameterInfo.IsOut && !string.IsNullOrEmpty(GenerateCSharpManagedToNativeConverter(buildData, parameterInfo.Type, caller))) if (parameterInfo.IsOut && !string.IsNullOrEmpty(GenerateCSharpManagedToNativeConverter(buildData, parameterInfo.Type, caller)))
@@ -736,7 +840,7 @@ namespace Flax.Build.Bindings
if (parameterInfo.IsOut && parameterInfo.DefaultValue == "var __resultAsRef") if (parameterInfo.IsOut && parameterInfo.DefaultValue == "var __resultAsRef")
{ {
// TODO: make this code shared with MarshalUsing selection from the above // TODO: make this code shared with MarshalUsing selection from the above
if (parameterInfo.Type.Type == "Array" || parameterInfo.Type.Type == "Span" || parameterInfo.Type.Type == "DataContainer" || parameterInfo.Type.Type == "BytesContainer") if (parameterInfo.Type.IsArrayOrSpan)
parameterMarshalType = $"MarshalUsing(typeof(FlaxEngine.Interop.ArrayMarshaller<,>), CountElementName = \"{parameterInfo.Name}Count\")"; parameterMarshalType = $"MarshalUsing(typeof(FlaxEngine.Interop.ArrayMarshaller<,>), CountElementName = \"{parameterInfo.Name}Count\")";
else if (parameterInfo.Type.Type == "Dictionary") else if (parameterInfo.Type.Type == "Dictionary")
parameterMarshalType = "MarshalUsing(typeof(FlaxEngine.Interop.DictionaryMarshaller<,>), ConstantElementCount = 0)"; parameterMarshalType = "MarshalUsing(typeof(FlaxEngine.Interop.DictionaryMarshaller<,>), ConstantElementCount = 0)";
@@ -754,7 +858,12 @@ namespace Flax.Build.Bindings
if (parameterInfo.IsOut) if (parameterInfo.IsOut)
contents.Append("out "); contents.Append("out ");
else if (parameterInfo.IsRef || UsePassByReference(buildData, parameterInfo.Type, caller)) else if (parameterInfo.IsRef || UsePassByReference(buildData, parameterInfo.Type, caller))
contents.Append("ref "); {
if (parameterInfo.IsConst || parameterInfo.Type.IsConst)
contents.Append("in ");
else
contents.Append("/*xcvg1*/ ref ");
}
contents.Append(nativeType); contents.Append(nativeType);
contents.Append(' '); contents.Append(' ');
contents.Append(parameterInfo.Name); contents.Append(parameterInfo.Name);
@@ -802,7 +911,17 @@ namespace Flax.Build.Bindings
if (parameterInfo.IsOut) if (parameterInfo.IsOut)
contents.Append("out "); contents.Append("out ");
else if (parameterInfo.IsRef || UsePassByReference(buildData, parameterInfo.Type, caller)) else if (parameterInfo.IsRef || UsePassByReference(buildData, parameterInfo.Type, caller))
contents.Append("ref "); {
if (isSetter || parameterInfo.Type.IsConst)
contents.Append("in ");
else
{
//CanUpdate(BehaviorUpdateContext
if (parameterInfo.Name == "context" && functionInfo.Name == "CanUpdate")
separator = separator;
contents.Append("/*xcvg2*/ ref ");
}
}
var convertFunc = GenerateCSharpManagedToNativeConverter(buildData, parameterInfo.Type, caller); var convertFunc = GenerateCSharpManagedToNativeConverter(buildData, parameterInfo.Type, caller);
var paramName = isSetter ? "value" : parameterInfo.Name; var paramName = isSetter ? "value" : parameterInfo.Name;
@@ -836,7 +955,12 @@ namespace Flax.Build.Bindings
if (parameterInfo.IsOut) if (parameterInfo.IsOut)
contents.Append("out "); contents.Append("out ");
else if (parameterInfo.IsRef || UsePassByReference(buildData, parameterInfo.Type, caller)) else if (parameterInfo.IsRef || UsePassByReference(buildData, parameterInfo.Type, caller))
{
if (parameterInfo.IsConst ||parameterInfo.Type.IsConst)
contents.Append("in ");
else
contents.Append("ref "); contents.Append("ref ");
}
// Pass value // Pass value
contents.Append(parameterInfo.DefaultValue); contents.Append(parameterInfo.DefaultValue);
@@ -1369,7 +1493,12 @@ namespace Flax.Build.Bindings
if (parameterInfo.IsOut) if (parameterInfo.IsOut)
contents.Append("out "); contents.Append("out ");
else if (parameterInfo.IsRef) else if (parameterInfo.IsRef)
contents.Append("ref "); {
if (parameterInfo.IsConst || parameterInfo.Type.IsConst)
contents.Append("in ");
else
contents.Append("/*faffaf1*/ ref ");
}
else if (parameterInfo.IsThis) else if (parameterInfo.IsThis)
contents.Append("this "); contents.Append("this ");
else if (parameterInfo.IsParams) else if (parameterInfo.IsParams)
@@ -1431,7 +1560,7 @@ namespace Flax.Build.Bindings
if (parameterInfo.IsOut) if (parameterInfo.IsOut)
contents.Append("out "); contents.Append("out ");
else if (parameterInfo.IsRef) else if (parameterInfo.IsRef)
contents.Append("ref "); contents.Append("/*faffaf2*/ ref ");
else if (parameterInfo.IsThis) else if (parameterInfo.IsThis)
contents.Append("this "); contents.Append("this ");
else if (parameterInfo.IsParams) else if (parameterInfo.IsParams)
@@ -1522,8 +1651,8 @@ namespace Flax.Build.Bindings
public static class ManagedToNative public static class ManagedToNative
{ {
public static {{classInfo.Name}} ConvertToManaged(IntPtr unmanaged) => Unsafe.As<{{classInfo.Name}}>(ManagedHandleMarshaller.NativeToManaged.ConvertToManaged(unmanaged)); public static {{classInfo.Name}} ConvertToManaged(IntPtr unmanaged) => Unsafe.As<{{classInfo.Name}}>(ManagedHandleMarshaller.NativeToManaged.ConvertToManaged(unmanaged));
public static IntPtr ConvertToUnmanaged({{classInfo.Name}} managed) => managed != null ? ManagedHandle.ToIntPtr(managed, GCHandleType.Weak) : IntPtr.Zero; public static IntPtr ConvertToUnmanaged({{classInfo.Name}} managed) => managed != null ? ManagedHandle.ToIntPtr(managed/*, GCHandleType.Weak*/) : IntPtr.Zero;
public static void Free(IntPtr unmanaged) {} public static void Free(IntPtr unmanaged) => ManagedHandleMarshaller.Free(unmanaged);
} }
#if FLAX_EDITOR #if FLAX_EDITOR
[HideInEditor] [HideInEditor]
@@ -1708,7 +1837,7 @@ namespace Flax.Build.Bindings
{ {
var managedType = GenerateCSharpNativeToManaged(buildData, marshalType.GenericArgs[0], structureInfo); var managedType = GenerateCSharpNativeToManaged(buildData, marshalType.GenericArgs[0], structureInfo);
toManagedContent.AppendLine($"unmanaged.{fieldInfo.Name} != IntPtr.Zero ? Unsafe.As<{managedType}>(ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Target) : null;"); toManagedContent.AppendLine($"unmanaged.{fieldInfo.Name} != IntPtr.Zero ? Unsafe.As<{managedType}>(ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Target) : null;");
toNativeContent.AppendLine($"managed.{fieldInfo.Name} != null ? ManagedHandle.ToIntPtr(managed.{fieldInfo.Name}, GCHandleType.Weak) : IntPtr.Zero;"); toNativeContent.AppendLine($"managed.{fieldInfo.Name} != null ? ManagedHandle.ToIntPtr(managed.{fieldInfo.Name}/*, GCHandleType.Weak*/) : IntPtr.Zero; // plaa 1");
freeContents.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Free(); }}"); freeContents.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Free(); }}");
// Permanent ScriptingObject handle is passed from native side, do not release it // Permanent ScriptingObject handle is passed from native side, do not release it
@@ -1717,7 +1846,7 @@ namespace Flax.Build.Bindings
else if (marshalType.Type == "ScriptingObject") else if (marshalType.Type == "ScriptingObject")
{ {
toManagedContent.AppendLine($"unmanaged.{fieldInfo.Name} != IntPtr.Zero ? Unsafe.As<FlaxEngine.Object>(ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Target) : null;"); toManagedContent.AppendLine($"unmanaged.{fieldInfo.Name} != IntPtr.Zero ? Unsafe.As<FlaxEngine.Object>(ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Target) : null;");
toNativeContent.AppendLine($"managed.{fieldInfo.Name} != null ? ManagedHandle.ToIntPtr(managed.{fieldInfo.Name}, GCHandleType.Weak) : IntPtr.Zero;"); toNativeContent.AppendLine($"managed.{fieldInfo.Name} != null ? ManagedHandle.ToIntPtr(managed.{fieldInfo.Name}/*, GCHandleType.Weak*/) : IntPtr.Zero; // plaa 2");
freeContents.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Free(); }}"); freeContents.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Free(); }}");
// Permanent ScriptingObject handle is passed from native side, do not release it // Permanent ScriptingObject handle is passed from native side, do not release it
@@ -1726,7 +1855,7 @@ namespace Flax.Build.Bindings
else if (marshalType.IsPtr && originalType != "IntPtr" && !originalType.EndsWith("*")) else if (marshalType.IsPtr && originalType != "IntPtr" && !originalType.EndsWith("*"))
{ {
toManagedContent.AppendLine($"unmanaged.{fieldInfo.Name} != IntPtr.Zero ? Unsafe.As<{originalType}>(ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Target) : null;"); toManagedContent.AppendLine($"unmanaged.{fieldInfo.Name} != IntPtr.Zero ? Unsafe.As<{originalType}>(ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Target) : null;");
toNativeContent.AppendLine($"managed.{fieldInfo.Name} != null ? ManagedHandle.ToIntPtr(managed.{fieldInfo.Name}, GCHandleType.Weak) : IntPtr.Zero;"); toNativeContent.AppendLine($"managed.{fieldInfo.Name} != null ? ManagedHandle.ToIntPtr(managed.{fieldInfo.Name}/*, GCHandleType.Weak*/) : IntPtr.Zero; // plaa 3");
freeContents.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Free(); }}"); freeContents.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Free(); }}");
// Permanent ScriptingObject handle is passed from native side, do not release it // Permanent ScriptingObject handle is passed from native side, do not release it
@@ -1735,7 +1864,7 @@ namespace Flax.Build.Bindings
else if (marshalType.Type == "Dictionary") else if (marshalType.Type == "Dictionary")
{ {
toManagedContent.AppendLine($"unmanaged.{fieldInfo.Name} != IntPtr.Zero ? Unsafe.As<{originalType}>(ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Target) : null;"); toManagedContent.AppendLine($"unmanaged.{fieldInfo.Name} != IntPtr.Zero ? Unsafe.As<{originalType}>(ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Target) : null;");
toNativeContent.AppendLine($"ManagedHandle.ToIntPtr(managed.{fieldInfo.Name}, GCHandleType.Weak);"); toNativeContent.AppendLine($"ManagedHandle.ToIntPtr(managed.{fieldInfo.Name}/*, GCHandleType.Weak*/);");
freeContents.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Free(); }}"); freeContents.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Free(); }}");
freeContents2.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Free(); }}"); freeContents2.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Free(); }}");
} }
@@ -1757,7 +1886,7 @@ namespace Flax.Build.Bindings
{ {
// Array elements passed as GCHandles // Array elements passed as GCHandles
toManagedContent.AppendLine($"unmanaged.{fieldInfo.Name} != IntPtr.Zero ? NativeInterop.GCHandleArrayToManagedArray<{originalElementType}>(Unsafe.As<ManagedArray>(ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Target)) : null;"); toManagedContent.AppendLine($"unmanaged.{fieldInfo.Name} != IntPtr.Zero ? NativeInterop.GCHandleArrayToManagedArray<{originalElementType}>(Unsafe.As<ManagedArray>(ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Target)) : null;");
toNativeContent.AppendLine($"managed.{fieldInfo.Name}?.Length > 0 ? ManagedHandle.ToIntPtr(NativeInterop.ManagedArrayToGCHandleWrappedArray(managed.{fieldInfo.Name}), GCHandleType.Weak) : IntPtr.Zero;"); toNativeContent.AppendLine($"managed.{fieldInfo.Name}?.Length > 0 ? ManagedHandle.ToIntPtr(NativeInterop.ManagedArrayToGCHandleWrappedArray(managed.{fieldInfo.Name})/*, GCHandleType.Weak*/) : IntPtr.Zero;");
freeContents.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle handle = ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}); Span<IntPtr> ptrs = (Unsafe.As<ManagedArray>(handle.Target)).ToSpan<IntPtr>(); foreach (var ptr in ptrs) {{ if (ptr != IntPtr.Zero) {{ ManagedHandle.FromIntPtr(ptr).Free(); }} }} (Unsafe.As<ManagedArray>(handle.Target)).Free(); handle.Free(); }}"); freeContents.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle handle = ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}); Span<IntPtr> ptrs = (Unsafe.As<ManagedArray>(handle.Target)).ToSpan<IntPtr>(); foreach (var ptr in ptrs) {{ if (ptr != IntPtr.Zero) {{ ManagedHandle.FromIntPtr(ptr).Free(); }} }} (Unsafe.As<ManagedArray>(handle.Target)).Free(); handle.Free(); }}");
// Permanent ScriptingObject handle is passed from native side, do not release it // Permanent ScriptingObject handle is passed from native side, do not release it
@@ -1775,7 +1904,7 @@ namespace Flax.Build.Bindings
else if (marshalType.Type == "Version") else if (marshalType.Type == "Version")
{ {
toManagedContent.AppendLine($"unmanaged.{fieldInfo.Name} != IntPtr.Zero ? Unsafe.As<{originalType}>(ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Target) : null;"); toManagedContent.AppendLine($"unmanaged.{fieldInfo.Name} != IntPtr.Zero ? Unsafe.As<{originalType}>(ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Target) : null;");
toNativeContent.AppendLine($"ManagedHandle.ToIntPtr(managed.{fieldInfo.Name}, GCHandleType.Weak);"); toNativeContent.AppendLine($"ManagedHandle.ToIntPtr(managed.{fieldInfo.Name}/*, GCHandleType.Weak*/);");
freeContents.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Free(); }}"); freeContents.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Free(); }}");
freeContents2.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Free(); }}"); freeContents2.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Free(); }}");
} }
@@ -1824,66 +1953,77 @@ namespace Flax.Build.Bindings
toManagedContent.Append("return managed;"); toManagedContent.Append("return managed;");
} }
string hideInEditorAttribute = (buildData.Target != null & buildData.Target.IsEditor) ? "[HideInEditor]" : "";
string marshalManagedType = structureInfo.Name;
string marshalNativeType = $"{structureInfo.Name}Internal";
contents.AppendLine(string.Join(Environment.NewLine + indent, (indent + $$""" contents.AppendLine(string.Join(Environment.NewLine + indent, (indent + $$"""
/// <summary> /// <summary>
/// Marshaller for type <see cref="{{structureInfo.Name}}"/>. /// Marshaller for type <see cref="{{marshalManagedType}}"/>.
/// </summary> /// </summary>
{{InsertHideInEditorSection()}} {{hideInEditorAttribute}}
[CustomMarshaller(typeof({{structureInfo.Name}}), MarshalMode.ManagedToUnmanagedIn, typeof({{marshallerFullName}}.ManagedToNative))] [CustomMarshaller(typeof({{marshalManagedType}}), MarshalMode.ManagedToUnmanagedIn, typeof({{marshallerFullName}}.ManagedToNative))]
[CustomMarshaller(typeof({{structureInfo.Name}}), MarshalMode.UnmanagedToManagedOut, typeof({{marshallerFullName}}.ManagedToNative))] [CustomMarshaller(typeof({{marshalManagedType}}), MarshalMode.UnmanagedToManagedOut, typeof({{marshallerFullName}}.ManagedToNative))]
[CustomMarshaller(typeof({{structureInfo.Name}}), MarshalMode.ElementIn, typeof({{marshallerFullName}}.ManagedToNative))] [CustomMarshaller(typeof({{marshalManagedType}}), MarshalMode.ElementIn, typeof({{marshallerFullName}}.ManagedToNative))]
[CustomMarshaller(typeof({{structureInfo.Name}}), MarshalMode.ManagedToUnmanagedOut, typeof({{marshallerFullName}}.NativeToManaged))] [CustomMarshaller(typeof({{marshalManagedType}}), MarshalMode.ManagedToUnmanagedOut, typeof({{marshallerFullName}}.NativeToManaged))]
[CustomMarshaller(typeof({{structureInfo.Name}}), MarshalMode.UnmanagedToManagedIn, typeof({{marshallerFullName}}.NativeToManaged))] [CustomMarshaller(typeof({{marshalManagedType}}), MarshalMode.UnmanagedToManagedIn, typeof({{marshallerFullName}}.NativeToManaged))]
[CustomMarshaller(typeof({{structureInfo.Name}}), MarshalMode.ElementOut, typeof({{marshallerFullName}}.NativeToManaged))] [CustomMarshaller(typeof({{marshalManagedType}}), MarshalMode.ElementOut, typeof({{marshallerFullName}}.NativeToManaged))]
[CustomMarshaller(typeof({{structureInfo.Name}}), MarshalMode.ManagedToUnmanagedRef, typeof({{marshallerFullName}}.Bidirectional))] [CustomMarshaller(typeof({{marshalManagedType}}), MarshalMode.ManagedToUnmanagedRef, typeof({{marshallerFullName}}.Bidirectional))]
[CustomMarshaller(typeof({{structureInfo.Name}}), MarshalMode.UnmanagedToManagedRef, typeof({{marshallerFullName}}.Bidirectional))] [CustomMarshaller(typeof({{marshalManagedType}}), MarshalMode.UnmanagedToManagedRef, typeof({{marshallerFullName}}.Bidirectional))]
[CustomMarshaller(typeof({{structureInfo.Name}}), MarshalMode.ElementRef, typeof({{marshallerFullName}}))] [CustomMarshaller(typeof({{marshalManagedType}}), MarshalMode.ElementRef, typeof({{marshallerFullName}}))]
{{GenerateCSharpAccessLevel(structureInfo.Access)}}static unsafe class {{marshallerName}} {{GenerateCSharpAccessLevel(structureInfo.Access)}}static unsafe class {{marshallerName}}
{ {
#pragma warning disable 1591 #pragma warning disable 1591
#pragma warning disable 618 #pragma warning disable 618
{{structContents.Replace("\n", Environment.NewLine + " ").ToString().TrimEnd()}} {{structContents.Replace("\n", Environment.NewLine + " ").ToString().TrimEnd()}}
{{InsertHideInEditorSection()}} {{hideInEditorAttribute}}
public static class NativeToManaged public static class NativeToManaged
{ {
public static {{structureInfo.Name}} ConvertToManaged({{structureInfo.Name}}Internal unmanaged) => {{marshallerFullName}}.ToManaged(unmanaged); public static {{marshalManagedType}} ConvertToManaged({{marshalNativeType}} unmanaged) => {{marshallerFullName}}.ToManaged(unmanaged);
public static {{structureInfo.Name}}Internal ConvertToUnmanaged({{structureInfo.Name}} managed) => {{marshallerFullName}}.ToNative(managed); public static {{marshalNativeType}} ConvertToUnmanaged({{marshalManagedType}} managed) => {{marshallerFullName}}.ToNative(managed);
public static void Free({{structureInfo.Name}}Internal unmanaged) public static void Free({{marshalNativeType}} unmanaged)
{ {
{{freeContents2.Replace("\n", Environment.NewLine + " ").ToString().TrimEnd()}} {{freeContents2.Replace("\n", Environment.NewLine + " ").ToString().TrimEnd()}}
} }
} }
{{InsertHideInEditorSection()}} {{hideInEditorAttribute}}
public static class ManagedToNative public static class ManagedToNative
{ {
public static {{structureInfo.Name}} ConvertToManaged({{structureInfo.Name}}Internal unmanaged) => {{marshallerFullName}}.ToManaged(unmanaged); public static {{marshalManagedType}} ConvertToManaged({{marshalNativeType}} unmanaged) => {{marshallerFullName}}.ToManaged(unmanaged);
public static {{structureInfo.Name}}Internal ConvertToUnmanaged({{structureInfo.Name}} managed) => {{marshallerFullName}}.ToNative(managed); public static {{marshalNativeType}} ConvertToUnmanaged({{marshalManagedType}} managed) => {{marshallerFullName}}.ToNative(managed);
public static void Free({{structureInfo.Name}}Internal unmanaged) => {{marshallerFullName}}.Free(unmanaged); public static void Free({{marshalNativeType}} unmanaged) => {{marshallerFullName}}.Free(unmanaged);
} }
{{InsertHideInEditorSection()}} {{hideInEditorAttribute}}
public struct Bidirectional public ref struct Bidirectional
{ {
{{structureInfo.Name}} managed; {{marshalManagedType}} managed;
{{structureInfo.Name}}Internal unmanaged; {{marshalNativeType}} unmanaged;
public void FromManaged({{structureInfo.Name}} managed) => this.managed = managed; public void FromManaged({{marshalManagedType}} managed) => this.managed = managed;
public {{structureInfo.Name}}Internal ToUnmanaged() { unmanaged = {{marshallerFullName}}.ToNative(managed); return unmanaged; } public {{marshalNativeType}} ToUnmanaged() { unmanaged = {{marshallerFullName}}.ToNative(managed); return unmanaged; }
public void FromUnmanaged({{structureInfo.Name}}Internal unmanaged) => this.unmanaged = unmanaged; public void FromUnmanaged({{marshalNativeType}} unmanaged)
public {{structureInfo.Name}} ToManaged() { managed = {{marshallerFullName}}.ToManaged(unmanaged); return managed; } {
public void Free() => NativeToManaged.Free(unmanaged); if (!unmanaged.Equals(this.unmanaged))
{{marshallerName}}.Free(this.unmanaged); // Release temporary handles before replacing them with permanent handles
this.unmanaged = unmanaged;
} }
internal static {{structureInfo.Name}} ConvertToManaged({{structureInfo.Name}}Internal unmanaged) => ToManaged(unmanaged); public {{marshalManagedType}} ToManaged() { managed = {{marshallerFullName}}.ToManaged(unmanaged); return managed; }
internal static {{structureInfo.Name}}Internal ConvertToUnmanaged({{structureInfo.Name}} managed) => ToNative(managed); public void Free()
internal static void Free({{structureInfo.Name}}Internal unmanaged) {
NativeToManaged.Free(unmanaged);
}
}
internal static {{marshalManagedType}} ConvertToManaged({{marshalNativeType}} unmanaged) => ToManaged(unmanaged);
internal static {{marshalNativeType}} ConvertToUnmanaged({{marshalManagedType}} managed) => ToNative(managed);
internal static void Free({{marshalNativeType}} unmanaged)
{ {
{{freeContents.Replace("\n", Environment.NewLine + " ").ToString().TrimEnd()}} {{freeContents.Replace("\n", Environment.NewLine + " ").ToString().TrimEnd()}}
} }
internal static {{structureInfo.Name}} ToManaged({{structureInfo.Name}}Internal unmanaged) internal static {{marshalManagedType}} ToManaged({{marshalNativeType}} unmanaged)
{ {
{{toManagedContent.Replace("\n", Environment.NewLine + " ").ToString().TrimEnd()}} {{toManagedContent.Replace("\n", Environment.NewLine + " ").ToString().TrimEnd()}}
} }
internal static {{structureInfo.Name}}Internal ToNative({{structureInfo.Name}} managed) internal static {{marshalNativeType}} ToNative({{marshalManagedType}} managed)
{ {
{{toNativeContent.Replace("\n", Environment.NewLine + " ").ToString().TrimEnd()}} {{toNativeContent.Replace("\n", Environment.NewLine + " ").ToString().TrimEnd()}}
} }
@@ -1892,13 +2032,6 @@ namespace Flax.Build.Bindings
} }
""").Split(new char[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries))); """).Split(new char[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries)));
string InsertHideInEditorSection()
{
return (buildData.Target != null & buildData.Target.IsEditor) ? $$"""
[HideInEditor]
""" : "";
}
PutStringBuilder(toManagedContent); PutStringBuilder(toManagedContent);
PutStringBuilder(toNativeContent); PutStringBuilder(toNativeContent);
PutStringBuilder(freeContents); PutStringBuilder(freeContents);
@@ -2231,7 +2364,7 @@ namespace Flax.Build.Bindings
if (parameterInfo.IsOut) if (parameterInfo.IsOut)
contents.Append("out "); contents.Append("out ");
else if (parameterInfo.IsRef) else if (parameterInfo.IsRef)
contents.Append("ref "); contents.Append("/*faffaf3*/ ref ");
else if (parameterInfo.IsThis) else if (parameterInfo.IsThis)
contents.Append("this "); contents.Append("this ");
else if (parameterInfo.IsParams) else if (parameterInfo.IsParams)

View File

@@ -119,7 +119,8 @@ namespace Flax.Build.Bindings
// Pass as pointer to local variable converted for managed runtime // Pass as pointer to local variable converted for managed runtime
if (paramType.IsPtr) if (paramType.IsPtr)
result = string.Format(nativeToManaged, '*' + paramName); result = string.Format(nativeToManaged, '*' + paramName);
contents.Append($" auto __param_{paramName} = {result};").AppendLine(); contents.Append($" auto __param_orig_{paramName} = {result};").AppendLine();
contents.Append($" auto __param_{paramName} = __param_orig_{paramName};").AppendLine();
result = $"&__param_{paramName}"; result = $"&__param_{paramName}";
useLocalVar = true; useLocalVar = true;
} }
@@ -387,17 +388,7 @@ namespace Flax.Build.Bindings
// Find namespace for this type to build a fullname // Find namespace for this type to build a fullname
if (apiType != null) if (apiType != null)
{ managedType = apiType.Namespace + '.' + managedType.Replace(".", "+");
var e = apiType.Parent;
while (!(e is FileInfo))
{
e = e.Parent;
}
if (e is FileInfo fileInfo && !managedType.StartsWith(fileInfo.Namespace))
{
managedType = fileInfo.Namespace + '.' + managedType.Replace(".", "+");
}
}
// Use runtime lookup from fullname of the C# class // Use runtime lookup from fullname of the C# class
return "Scripting::FindClass(\"" + managedType + "\")"; return "Scripting::FindClass(\"" + managedType + "\")";
@@ -540,14 +531,25 @@ namespace Flax.Build.Bindings
if ((typeInfo.Type == "Array" || typeInfo.Type == "Span" || typeInfo.Type == "DataContainer") && typeInfo.GenericArgs != null) if ((typeInfo.Type == "Array" || typeInfo.Type == "Span" || typeInfo.Type == "DataContainer") && typeInfo.GenericArgs != null)
{ {
var arrayTypeInfo = typeInfo.GenericArgs[0]; var arrayTypeInfo = typeInfo.GenericArgs[0];
#if USE_NETCORE
// Boolean arrays does not support custom marshalling for some unknown reason
if (arrayTypeInfo.Type == "bool")
{
type = "bool*";
return "MUtils::ToBoolArray({0})";
}
var arrayApiType = FindApiTypeInfo(buildData, arrayTypeInfo, caller); var arrayApiType = FindApiTypeInfo(buildData, arrayTypeInfo, caller);
#if USE_NETCORE
if (arrayApiType?.IsPod ?? false)
{
if (functionInfo == null)
{
// Storage type should be wrapped
type = $"NativeArray<{arrayApiType.FullNameNative}>";
return $"MUtils::ToNativeArrayWrapper<{arrayApiType.FullNameNative}>({{0}})/*sahho*/";
}
else
{
//type = arrayApiType.FullNameNative + "*/*wahho*/";
type = $"NativeArray<{arrayApiType.FullNameNative}>/*tahho*/";
//if (arrayTypeInfo.Type == "bool")
// return "MUtils::ToBoolArray({0})/*bahho*/";
return "MUtils::ToNativeArrayWrapper({0})/*nahhoa7eatra*/";
}
}
#endif #endif
type = "MArray*"; type = "MArray*";
if (arrayApiType != null && arrayApiType.MarshalAs != null) if (arrayApiType != null && arrayApiType.MarshalAs != null)
@@ -563,12 +565,14 @@ namespace Flax.Build.Bindings
return "MUtils::ToArray({0}, " + GenerateCppGetMClass(buildData, arrayTypeInfo, caller, functionInfo) + ")"; return "MUtils::ToArray({0}, " + GenerateCppGetMClass(buildData, arrayTypeInfo, caller, functionInfo) + ")";
} }
#if !USE_NETCORE
// Span // Span
if (typeInfo.Type == "Span" && typeInfo.GenericArgs != null) if (typeInfo.Type == "Span" && typeInfo.GenericArgs != null)
{ {
type = "MonoArray*"; type = "MonoArray*";
return "MUtils::Span({0}, " + GenerateCppGetMClass(buildData, typeInfo.GenericArgs[0], caller, functionInfo) + ")"; return "MUtils::Span({0}, " + GenerateCppGetMClass(buildData, typeInfo.GenericArgs[0], caller, functionInfo) + ")";
} }
#endif
// BytesContainer // BytesContainer
if (typeInfo.Type == "BytesContainer" && typeInfo.GenericArgs == null) if (typeInfo.Type == "BytesContainer" && typeInfo.GenericArgs == null)
@@ -599,7 +603,7 @@ namespace Flax.Build.Bindings
{ {
CppIncludeFiles.Add("Engine/Scripting/Internal/ManagedBitArray.h"); CppIncludeFiles.Add("Engine/Scripting/Internal/ManagedBitArray.h");
#if USE_NETCORE #if USE_NETCORE
// Boolean arrays does not support custom marshalling for some unknown reason // Special case for copying bits to bytes
type = "bool*"; type = "bool*";
return "MUtils::ToBoolArray({0})"; return "MUtils::ToBoolArray({0})";
#else #else
@@ -751,6 +755,7 @@ namespace Flax.Build.Bindings
// Array // Array
if (typeInfo.Type == "Array" && typeInfo.GenericArgs != null) if (typeInfo.Type == "Array" && typeInfo.GenericArgs != null)
{ {
needLocalVariable = true;
var arrayTypeInfo = typeInfo.GenericArgs[0]; var arrayTypeInfo = typeInfo.GenericArgs[0];
var arrayApiType = FindApiTypeInfo(buildData, arrayTypeInfo, caller); var arrayApiType = FindApiTypeInfo(buildData, arrayTypeInfo, caller);
if (arrayApiType != null && arrayApiType.MarshalAs != null) if (arrayApiType != null && arrayApiType.MarshalAs != null)
@@ -770,6 +775,16 @@ namespace Flax.Build.Bindings
genericArgs += ", " + typeInfo.GenericArgs[1]; genericArgs += ", " + typeInfo.GenericArgs[1];
result = $"Array<{genericArgs}>({result})"; result = $"Array<{genericArgs}>({result})";
} }
else if (arrayApiType?.IsPod ?? false)
{
type = arrayApiType.FullNameNative + '*';
result = $"Array<{genericArgs}>(({arrayApiType.FullNameNative}*){{0}}, {{1}})/*xviox1 {arrayTypeInfo.Type}*/";
}
else if (arrayApiType?.Name == "bool")
{
type = "bool*";
result = "Array<bool>({0}, {1})/*xviox2*/\"";
}
return result; return result;
} }
@@ -779,11 +794,20 @@ namespace Flax.Build.Bindings
type = "MArray*"; type = "MArray*";
// Scripting Objects pointers has to be converted from managed object pointer into native object pointer to use Array converted for this // Scripting Objects pointers has to be converted from managed object pointer into native object pointer to use Array converted for this
var t = FindApiTypeInfo(buildData, typeInfo.GenericArgs[0], caller); var arrayApiType = FindApiTypeInfo(buildData, typeInfo.GenericArgs[0], caller);
if (typeInfo.GenericArgs[0].IsPtr && t != null && t.IsScriptingObject) if (typeInfo.GenericArgs[0].IsPtr && arrayApiType != null && arrayApiType.IsScriptingObject)
{ {
// TODO: array needs to be freed
return "MUtils::ToSpan<" + typeInfo.GenericArgs[0] + ">(" + "MUtils::ToArray<" + typeInfo.GenericArgs[0] + ">({0}))"; return "MUtils::ToSpan<" + typeInfo.GenericArgs[0] + ">(" + "MUtils::ToArray<" + typeInfo.GenericArgs[0] + ">({0}))";
} }
else if (arrayApiType?.IsPod ?? false)
{
type = arrayApiType.FullNameNative + '*';
var nativeName = arrayApiType.FullNameNative;
if (typeInfo.GenericArgs[0].IsConst)
nativeName = "const " + nativeName;
return $"Span<{nativeName}>({{0}}, {{1}})/*spantag */";
}
return "MUtils::ToSpan<" + typeInfo.GenericArgs[0] + ">({0})"; return "MUtils::ToSpan<" + typeInfo.GenericArgs[0] + ">({0})";
} }
@@ -998,6 +1022,11 @@ namespace Flax.Build.Bindings
} }
#endif #endif
if (functionInfo.UniqueName == "SetView" && callerName == "SceneRenderTaskInternal")
callerName = callerName;
if (functionInfo.UniqueName == "SetDriveControl")
callerName = callerName;
// Setup function binding glue to ensure that wrapper method signature matches for C++ and C# // Setup function binding glue to ensure that wrapper method signature matches for C++ and C#
functionInfo.Glue = new FunctionInfo.GlueInfo functionInfo.Glue = new FunctionInfo.GlueInfo
{ {
@@ -1155,6 +1184,7 @@ namespace Flax.Build.Bindings
}, },
IsOut = parameterInfo.IsOut, IsOut = parameterInfo.IsOut,
IsRef = isRefOut || parameterInfo.Type.IsRef, IsRef = isRefOut || parameterInfo.Type.IsRef,
IsConst = true,
}); });
} }
#endif #endif
@@ -1244,8 +1274,16 @@ namespace Flax.Build.Bindings
callParams += ", "; callParams += ", ";
separator = true; separator = true;
var name = parameterInfo.Name; var name = parameterInfo.Name;
var countParamName = $"__{parameterInfo.Name}Count";
if (CppParamsThatNeedConversion[i] && (!FindApiTypeInfo(buildData, parameterInfo.Type, caller)?.IsStruct ?? false)) if (CppParamsThatNeedConversion[i] && (!FindApiTypeInfo(buildData, parameterInfo.Type, caller)?.IsStruct ?? false))
{
name = '*' + name; name = '*' + name;
countParamName = '*' + countParamName + "/*plop*/";
}
else if (parameterInfo.Type.IsRef)
{
countParamName = '*' + countParamName + "/*plipa*/";
}
string param = string.Empty; string param = string.Empty;
if (string.IsNullOrWhiteSpace(CppParamsWrappersCache[i])) if (string.IsNullOrWhiteSpace(CppParamsWrappersCache[i]))
@@ -1258,7 +1296,7 @@ namespace Flax.Build.Bindings
else else
{ {
// Convert value // Convert value
param += string.Format(CppParamsWrappersCache[i], name); param += string.Format(CppParamsWrappersCache[i], name, countParamName);
} }
// Special case for output result parameters that needs additional converting from native to managed format (such as non-POD structures or output array parameter) // Special case for output result parameters that needs additional converting from native to managed format (such as non-POD structures or output array parameter)
@@ -1293,11 +1331,21 @@ namespace Flax.Build.Bindings
callParams += parameterInfo.Name; callParams += parameterInfo.Name;
callParams += "Temp"; callParams += "Temp";
} }
// Instruct for more optoimized value move operation // Instruct for more optimized value move operation
else if (parameterInfo.Type.IsMoveRef) else if (parameterInfo.Type.IsMoveRef)
{ {
callParams += $"MoveTemp({param})"; callParams += $"MoveTemp({param})";
} }
else if (parameterInfo.Type.IsRef && !parameterInfo.Type.IsConst)
{
// Non-const lvalue reference parameters needs to be passed via temporary value
if (parameterInfo.IsOut || parameterInfo.IsRef)
contents.Append(indent).AppendFormat("{2}& {0}Temp = {1};", parameterInfo.Name, param, parameterInfo.Type.ToString(false)).AppendLine();
else
contents.Append(indent).AppendFormat("{2} {0}Temp = {1};", parameterInfo.Name, param, parameterInfo.Type.ToString(false)).AppendLine();
callParams += parameterInfo.Name;
callParams += "Temp";
}
else else
{ {
callParams += param; callParams += param;
@@ -1332,6 +1380,25 @@ namespace Flax.Build.Bindings
contents.Append(';'); contents.Append(';');
contents.AppendLine(); contents.AppendLine();
#if false
for (var i = 0; i < functionInfo.Parameters.Count; i++)
{
var parameterInfo = functionInfo.Parameters[i];
if (CppParamsThatNeedConversion[i] || CppParamsThatNeedLocalVariable[i])
{
var apiType = FindApiTypeInfo(buildData, parameterInfo.Type, caller);
if (parameterInfo.Type.Type == "Array")
{
contents.Append(indent).AppendLine($"/* converthing: {parameterInfo.Name} Type == {parameterInfo.Type.Type} ({parameterInfo.Type.ToString()}) */");
contents.Append(indent).AppendLine($"/* converthing2: {parameterInfo.Name} IsArray == {parameterInfo.Type.IsArray} */");
contents.Append(indent).AppendLine($"/* converthing3: {parameterInfo.Name} apitype == {apiType?.Name ?? ""} */");
//contents.Append(indent).AppendLine($"MUtils::FreeManagedArray<{parameterInfo.Type.GenericArgs[0].ToString(false)}>({parameterInfo.Name}Temp);");
}
}
}
#endif
// Convert special parameters back to managed world // Convert special parameters back to managed world
if (!useInlinedReturn) if (!useInlinedReturn)
{ {
@@ -1343,6 +1410,7 @@ namespace Flax.Build.Bindings
if (CppParamsThatNeedConversion[i]) if (CppParamsThatNeedConversion[i])
{ {
var value = string.Format(CppParamsThatNeedConversionWrappers[i], parameterInfo.Name + "Temp"); var value = string.Format(CppParamsThatNeedConversionWrappers[i], parameterInfo.Name + "Temp");
var elementType = parameterInfo.Type.GenericArgs != null ? FindApiTypeInfo(buildData, parameterInfo.Type.GenericArgs[0], caller) : null;
// MObject* parameters returned by reference need write barrier for GC // MObject* parameters returned by reference need write barrier for GC
if (parameterInfo.IsOut) if (parameterInfo.IsOut)
@@ -1352,14 +1420,30 @@ namespace Flax.Build.Bindings
{ {
if (apiType.IsClass) if (apiType.IsClass)
{ {
contents.Append(indent).AppendFormat("MCore::GC::WriteRef({0}, (MObject*){1});", parameterInfo.Name, value).AppendLine();
#if USE_NETCORE #if USE_NETCORE
if (parameterInfo.Type.Type == "Array") if (parameterInfo.Type.Type == "Array")
{ {
if (elementType?.IsPod ?? false)
{
contents.Append(indent).Append($"// isgigs1a").AppendLine();
contents.Append(indent).Append($"*{parameterInfo.Name} = ({parameterInfo.Type.GenericArgs[0]}*)MCore::GC::AllocateMemory(sizeof({parameterInfo.Type.GenericArgs[0]}) * {parameterInfo.Name}Temp.Count());").AppendLine();
contents.Append(indent).Append($"Platform::MemoryCopy(*{parameterInfo.Name}, {parameterInfo.Name}Temp.Get(), sizeof({parameterInfo.Type.GenericArgs[0]}) * {parameterInfo.Name}Temp.Count());").AppendLine();
}
else
{
contents.Append(indent).Append($"// isgigs1b").AppendLine();
contents.Append(indent).AppendFormat("MCore::GC::WriteRef({0}, (MObject*){1});", parameterInfo.Name, value).AppendLine();
}
// Array marshallers need to know amount of items written in the buffer // Array marshallers need to know amount of items written in the buffer
contents.Append(indent).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();
} }
else
#endif #endif
{
contents.Append(indent).Append($"// isgigs2").AppendLine();
contents.Append(indent).AppendFormat("MCore::GC::WriteRef({0}, (MObject*){1});", parameterInfo.Name, value).AppendLine();
}
continue; continue;
} }
if (apiType.IsStruct && !apiType.IsPod) if (apiType.IsStruct && !apiType.IsPod)
@@ -1375,21 +1459,29 @@ namespace Flax.Build.Bindings
// BytesContainer // BytesContainer
if (parameterInfo.Type.Type == "BytesContainer" && parameterInfo.Type.GenericArgs == null) if (parameterInfo.Type.Type == "BytesContainer" && parameterInfo.Type.GenericArgs == null)
{ {
contents.Append(indent).Append($"// hshsf").AppendLine();
contents.Append(indent).AppendFormat("MCore::GC::WriteRef({0}, (MObject*){1});", parameterInfo.Name, value).AppendLine(); contents.Append(indent).AppendFormat("MCore::GC::WriteRef({0}, (MObject*){1});", parameterInfo.Name, value).AppendLine();
// Array marshallers need to know amount of items written in the buffer // Array marshallers need to know amount of items written in the buffer
contents.Append(indent).AppendFormat("*__{0}Count = {1}.Length();", parameterInfo.Name, parameterInfo.Name + "Temp").AppendLine(); //contents.Append(indent).AppendFormat("*__{0}Count = {1}.Length();", parameterInfo.Name, parameterInfo.Name + "Temp").AppendLine();
continue; //continue;
} }
else
throw new Exception($"Unsupported type of parameter '{parameterInfo}' in method '{functionInfo}' to be passed using 'out'"); throw new Exception($"Unsupported type of parameter '{parameterInfo}' in method '{functionInfo}' to be passed using 'out'");
} }
} }
else if (parameterInfo.Type.Type == "Array" && (elementType?.IsPod ?? false))
contents.Append(indent).AppendFormat("*{0} = {1}.data;", parameterInfo.Name, value).AppendLine();
else
contents.Append(indent).AppendFormat("*{0} = {1};", parameterInfo.Name, value).AppendLine(); contents.Append(indent).AppendFormat("*{0} = {1};", parameterInfo.Name, value).AppendLine();
#if USE_NETCORE #if USE_NETCORE
if (parameterInfo.Type.Type == "Array") if (parameterInfo.Type.Type == "Array" || (parameterInfo.Type.Type == "BytesContainer" && parameterInfo.Type.GenericArgs == null))
{ {
contents.Append(indent).Append("// hshshj").AppendLine();
// Array marshallers need to know amount of items written in the buffer // Array marshallers need to know amount of items written in the buffer
if (parameterInfo.Type.Type is "Span" or "BytesContainer")
contents.Append(indent).AppendFormat("*__{0}Count = {1}.Length();", parameterInfo.Name, parameterInfo.Name + "Temp").AppendLine();
else
contents.Append(indent).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 #endif
@@ -1550,7 +1642,8 @@ namespace Flax.Build.Bindings
if (paramIsRef) if (paramIsRef)
{ {
// Pass as pointer to value when using ref/out parameter // Pass as pointer to value when using ref/out parameter
contents.Append($" auto __param_{parameterInfo.Name} = {paramValue};").AppendLine(); contents.Append($" auto __param_orig_{parameterInfo.Name} = {paramValue};").AppendLine();
contents.Append($" auto __param_{parameterInfo.Name} = __param_orig_{parameterInfo.Name};").AppendLine();
paramValue = $"&__param_{parameterInfo.Name}"; paramValue = $"&__param_{parameterInfo.Name}";
useLocalVar = true; useLocalVar = true;
} }
@@ -1630,6 +1723,124 @@ namespace Flax.Build.Bindings
else if (!passAsParamPtr) else if (!passAsParamPtr)
paramValue = '*' + paramValue; paramValue = '*' + paramValue;
contents.Append($" {parameterInfo.Name} = {paramValue};").AppendLine(); contents.Append($" {parameterInfo.Name} = {paramValue};").AppendLine();
// Release temporary GCHandles
/*if (passAsParamPtr)
{
//contents.Append($" if (params[{i}] != (void*)&__param_{parameterInfo.Name})").AppendLine();
contents.Append($" {{").AppendLine();
contents.Append($" auto __param{i}_handle = *(MGCHandle*)&params[{i}];").AppendLine();
contents.Append($" MCore::GCHandle::Free(__param{i}_handle);").AppendLine();
contents.Append($" }}").AppendLine();
}
else
{
//contents.Append($" if (params[{i}] != (void*)&__param_{parameterInfo.Name})").AppendLine();
contents.Append($" {{").AppendLine();
contents.Append($" auto __param{i}_handle = *(MGCHandle*)&params[{i}];").AppendLine();
contents.Append($" MCore::GCHandle::Free(__param{i}_handle);").AppendLine();
contents.Append($" }}").AppendLine();
}*/
}
}
}
// Release GCHandles of boxed values
for (var i = 0; i < functionInfo.Parameters.Count; i++)
{
var parameterInfo = functionInfo.Parameters[i];
var paramValue = GenerateCppWrapperNativeToBox(buildData, parameterInfo.Type, classInfo, out var apiType, parameterInfo.Name);
var paramIsRef = parameterInfo.IsRef || parameterInfo.IsOut;
if (paramValue.Contains("MUtils::Box<")) // FIXME
{
if (paramIsRef)
{
//contents.Append($" MUtils::FreeManaged<{parameterInfo.Type.ToString(false)}>((MObject*)__param_{parameterInfo.Name});").AppendLine();
/*if (useThunk)
{
// Release the original handle
contents.Append($" if (__param_orig_{parameterInfo.Name} != __param_{parameterInfo.Name}) //asdf1a").AppendLine();
contents.Append($" MUtils::FreeManaged<{parameterInfo.Type.ToString(false)}>((MObject*)__param_{parameterInfo.Name});").AppendLine();
}*/
contents.Append($" MUtils::FreeManaged<{parameterInfo.Type.Type}>((MObject*)__param_{parameterInfo.Name});").AppendLine();
if (useThunk)
{
// Release the original handle
contents.Append($" if (__param_orig_{parameterInfo.Name} != __param_{ parameterInfo.Name})").AppendLine();
contents.Append($" MUtils::FreeManaged<{parameterInfo.Type.Type}>((MObject*)__param_orig_{parameterInfo.Name});").AppendLine();
}
}
else if (apiType != null && !apiType.IsInBuild)
{
// int: ispod, isvaluetype
// vector: ispod, isvaluetype, isstruct
// guid: ispod, isvaluetype, isstruct, isinbuild
contents.Append($" MUtils::FreeManaged<{parameterInfo.Type.Type}>((MObject*)params[{i}]); //asdf1b").AppendLine();
//contents.Append($" auto __param{i}_handle = *(MGCHandle*)&params[{i}]; // asdf1b").AppendLine();
//contents.Append($" ASSERT((((unsigned long long)__param{i}_handle & 0xC000000000000000) >> 62) == 0);").AppendLine();
//contents.Append($" MCore::GCHandle::Free(__param{i}_handle);").AppendLine();
}
else if (apiType != null && !apiType.IsValueType)
{
if (parameterInfo.Type.Type.ToLower().Contains("string"))
apiType = apiType;
contents.Append($" MUtils::FreeManaged<{parameterInfo.Type.Type}>((MObject*)params[{i}]); //asdf1d").AppendLine();
}
else //if (apiType != null)
{
if (parameterInfo.Type.Type.ToLower().Contains("string"))
apiType = apiType;
//contents.Append($" //asdf1c {parameterInfo.Type.Type}").AppendLine();
contents.Append($" auto __param{i}_handle = *(MGCHandle*)&params[{i}]; // asdf1c").AppendLine();
//contents.Append($" ASSERT((((unsigned long long)__param{i}_handle & 0xC000000000000000) >> 62) == 0);").AppendLine();
contents.Append($" MCore::GCHandle::Free(__param{i}_handle);").AppendLine();
}
}
else if (paramValue.Contains("MUtils::ToArray(")) // FIXME
{
var genericType = parameterInfo.Type.GenericArgs[0].ToString(false);
if (paramIsRef)
{
//contents.Append($" MUtils::FreeManaged<{parameterInfo.Type.ToString(false)}>((MObject*)__param_{parameterInfo.Name});").AppendLine();
/*if (useThunk)
{
// Release the original handle
contents.Append($" if (__param_orig_{parameterInfo.Name} != __param_{parameterInfo.Name}) //asdf1a").AppendLine();
contents.Append($" MUtils::FreeManaged<{parameterInfo.Type.ToString(false)}>((MObject*)__param_{parameterInfo.Name});").AppendLine();
}*/
contents.Append($" MUtils::FreeManagedArray<{genericType}>(__param_{parameterInfo.Name}); //fgfgh3").AppendLine();
if (useThunk)
{
// Release the original handle
contents.Append($" if (__param_orig_{parameterInfo.Name} != __param_{ parameterInfo.Name})").AppendLine();
contents.Append($" MUtils::FreeManagedArray<{genericType}>(__param_orig_{parameterInfo.Name}); //fgfgh4").AppendLine();
}
}
else if (apiType != null && !apiType.IsInBuild)
{
// int: ispod, isvaluetype
// vector: ispod, isvaluetype, isstruct
// guid: ispod, isvaluetype, isstruct, isinbuild
contents.Append($" MUtils::FreeManagedArray<{genericType}>((MArray*)params[{i}]); //fgfgh5").AppendLine();
//contents.Append($" auto __param{i}_handle = *(MGCHandle*)&params[{i}]; // asdf1b").AppendLine();
//contents.Append($" ASSERT((((unsigned long long)__param{i}_handle & 0xC000000000000000) >> 62) == 0);").AppendLine();
//contents.Append($" MCore::GCHandle::Free(__param{i}_handle);").AppendLine();
}
else if (apiType != null && !apiType.IsValueType)
{
if (parameterInfo.Type.Type.ToLower().Contains("string"))
apiType = apiType;
contents.Append($" MUtils::FreeManagedArray<{genericType}>((MArray*)params[{i}]); //fgfgh6").AppendLine();
}
else //if (apiType != null)
{
if (parameterInfo.Type.Type.ToLower().Contains("string"))
apiType = apiType;
//contents.Append($" //asdf1c {parameterInfo.Type.Type}").AppendLine();
contents.Append($" auto __param{i}_handle = *(MGCHandle*)&params[{i}]; //fgfgh7").AppendLine();
//contents.Append($" ASSERT((((unsigned long long)__param{i}_handle & 0xC000000000000000) >> 62) == 0);").AppendLine();
contents.Append($" MCore::GCHandle::Free(__param{i}_handle);").AppendLine();
} }
} }
} }
@@ -2085,6 +2296,22 @@ namespace Flax.Build.Bindings
else if (!passAsParamPtr) else if (!passAsParamPtr)
paramValue = '*' + paramValue; paramValue = '*' + paramValue;
contents.Append($" arg{i} = {paramValue};").AppendLine(); contents.Append($" arg{i} = {paramValue};").AppendLine();
// Release temporary GCHandles
if (CppParamsThatNeedConversion[i])
{
// Release the original handle
contents.Append($" if (&__param_orig_arg{i} != &__param_arg{i})").AppendLine();
contents.Append($" FreeManaged(({managedType})__param_arg{i});").AppendLine();
}
/*contents.Append($" FreeManaged(({managedType})__param_arg{i}); //hmm1b").AppendLine();
if (CppParamsThatNeedConversion[i])
{
// Release the original handle
contents.Append($" if (&__param_orig_arg{i} != &__param_arg{i})").AppendLine();
contents.Append($" FreeManaged(({managedType})__param_orig_arg{i});").AppendLine();
}*/
paramType.IsRef = true; paramType.IsRef = true;
} }
} }
@@ -2382,6 +2609,7 @@ namespace Flax.Build.Bindings
{ {
Type = fieldInfo.Type, Type = fieldInfo.Type,
Name = "value", Name = "value",
IsConst = true,
}, },
}, },
ReturnType = new TypeInfo ReturnType = new TypeInfo
@@ -2461,14 +2689,20 @@ namespace Flax.Build.Bindings
contents.AppendLine(" }").AppendLine(); contents.AppendLine(" }").AppendLine();
// Unboxing structures from managed object to native data // Unboxing structures from managed object to native data
contents.AppendLine(" static void Unbox(void* ptr, MObject* managed)"); contents.AppendLine(" static void Unbox(void* ptr, MObject* data)");
contents.AppendLine(" {"); contents.AppendLine(" {");
if (structureInfo.MarshalAs != null) if (structureInfo.MarshalAs != null)
contents.AppendLine($" MISSING_CODE(\"Boxing native type {structureInfo.Name} as {structureInfo.MarshalAs}\");"); // TODO: impl this contents.AppendLine($" MISSING_CODE(\"Boxing native type {structureInfo.Name} as {structureInfo.MarshalAs}\");"); // TODO: impl this
else if (structureInfo.IsPod) else if (structureInfo.IsPod)
contents.AppendLine($" Platform::MemoryCopy(ptr, MCore::Object::Unbox(managed), sizeof({structureTypeNameNative}));"); contents.AppendLine($" MCore::Object::Unbox(data, ptr);");
//contents.AppendLine($" Platform::MemoryCopy(ptr, MCore::Object::Unbox(data), sizeof({structureTypeNameNative}));");
else else
contents.AppendLine($" *({structureTypeNameNative}*)ptr = ToNative(*({GenerateCppManagedWrapperName(structureInfo)}*)MCore::Object::Unbox(managed));"); {
contents.AppendLine($" {GenerateCppManagedWrapperName(structureInfo)} managed;");
contents.AppendLine($" MCore::Object::Unbox(data, &managed);");
contents.AppendLine($" *({structureTypeNameNative}*)ptr = ToNative(managed);");
}
//contents.AppendLine($" *({structureTypeNameNative}*)ptr = ToNative(*({GenerateCppManagedWrapperName(structureInfo)}*)MCore::Object::Unbox(data));");
contents.AppendLine(" }").AppendLine(); contents.AppendLine(" }").AppendLine();
} }
else else
@@ -2991,22 +3225,32 @@ namespace Flax.Build.Bindings
header.AppendLine("namespace {"); header.AppendLine("namespace {");
header.AppendFormat("{0} ToManaged(const {1}& value);", wrapperName, fullName).AppendLine(); header.AppendFormat("{0} ToManaged(const {1}& value);", wrapperName, fullName).AppendLine();
header.AppendFormat("{1} ToNative(const {0}& value);", wrapperName, fullName).AppendLine(); header.AppendFormat("{1} ToNative(const {0}& value);", wrapperName, fullName).AppendLine();
header.AppendFormat("void FreeManaged(const {0}& value);", wrapperName).AppendLine();
header.AppendLine("}"); header.AppendLine("}");
// Generate MConverter for a structure // Generate MConverter for a structure
header.Append("template<>").AppendLine(); header.Append("template<>").AppendLine();
header.AppendFormat("struct MConverter<{0}>", fullName).AppendLine(); header.AppendFormat("struct MConverter<{0}>", fullName).AppendLine();
header.Append('{').AppendLine(); header.Append('{').AppendLine();
header.AppendFormat(" MObject* Box(const {0}& data, const MClass* klass)", fullName).AppendLine();
header.AppendFormat(" DLLEXPORT USED MObject* Box(const {0}& data, const MClass* klass)", fullName).AppendLine();
header.Append(" {").AppendLine(); header.Append(" {").AppendLine();
header.Append(" auto managed = ToManaged(data);").AppendLine(); header.Append(" auto managed = ToManaged(data);").AppendLine();
header.Append(" return MCore::Object::Box((void*)&managed, klass);").AppendLine(); header.Append(" auto boxed = MCore::Object::Box((void*)&managed, klass);").AppendLine();
header.Append(" ::FreeManaged(managed);").AppendLine();
header.Append(" return boxed;").AppendLine();
header.Append(" }").AppendLine(); header.Append(" }").AppendLine();
header.AppendFormat(" void Unbox({0}& result, MObject* data)", fullName).AppendLine();
header.AppendFormat(" DLLEXPORT USED void Unbox({0}& result, MObject* data)", fullName).AppendLine();
header.Append(" {").AppendLine(); header.Append(" {").AppendLine();
header.AppendFormat(" result = ToNative(*reinterpret_cast<{0}*>(MCore::Object::Unbox(data)));", wrapperName).AppendLine(); //header.AppendFormat(" result = ToNative(*reinterpret_cast<{0}*>(MCore::Object::Unbox(data)));", wrapperName).AppendLine();
header.AppendFormat(" {0} managed;", wrapperName).AppendLine();
header.Append(" MCore::Object::Unbox(data, &managed);").AppendLine();
header.Append(" result = ToNative(managed);").AppendLine();
header.Append(" ::FreeManaged(managed);").AppendLine();
header.Append(" }").AppendLine(); header.Append(" }").AppendLine();
header.AppendFormat(" void ToManagedArray(MArray* result, const Span<{0}>& data)", fullName).AppendLine();
header.AppendFormat(" DLLEXPORT USED void ToManagedArray(MArray* result, const Span<{0}>& data)", fullName).AppendLine();
header.Append(" {").AppendLine(); header.Append(" {").AppendLine();
header.AppendFormat(" MClass* klass = {0}::TypeInitializer.GetClass();", fullName).AppendLine(); header.AppendFormat(" MClass* klass = {0}::TypeInitializer.GetClass();", fullName).AppendLine();
header.AppendFormat(" {0}* resultPtr = ({0}*)MCore::Array::GetAddress(result);", wrapperName).AppendLine(); header.AppendFormat(" {0}* resultPtr = ({0}*)MCore::Array::GetAddress(result);", wrapperName).AppendLine();
@@ -3016,12 +3260,39 @@ namespace Flax.Build.Bindings
header.Append(" MCore::GC::WriteValue(&resultPtr[i], &managed, 1, klass);").AppendLine(); header.Append(" MCore::GC::WriteValue(&resultPtr[i], &managed, 1, klass);").AppendLine();
header.Append(" }").AppendLine(); header.Append(" }").AppendLine();
header.Append(" }").AppendLine(); header.Append(" }").AppendLine();
header.AppendFormat(" void ToNativeArray(Span<{0}>& result, const MArray* data)", fullName).AppendLine();
header.AppendFormat(" DLLEXPORT USED void ToNativeArray(Span<{0}>& result, const MArray* data)", fullName).AppendLine();
header.Append(" {").AppendLine(); header.Append(" {").AppendLine();
header.AppendFormat(" {0}* dataPtr = ({0}*)MCore::Array::GetAddress(data);", wrapperName).AppendLine(); header.AppendFormat(" {0}* dataPtr = ({0}*)MCore::Array::GetAddress(data);", wrapperName).AppendLine();
header.Append(" for (int32 i = 0; i < result.Length(); i++)").AppendLine(); header.Append(" for (int32 i = 0; i < result.Length(); i++)").AppendLine();
header.Append(" result[i] = ToNative(dataPtr[i]);").AppendLine(); header.Append(" result[i] = ToNative(dataPtr[i]);").AppendLine();
header.Append(" }").AppendLine(); header.Append(" }").AppendLine();
header.AppendFormat(" DLLEXPORT USED void FreeManaged(MObject* data)").AppendLine();
header.Append(" {").AppendLine();
if (false) // FIXME: unboxing allocates temporary handles which FreeManaged does not free...
{
header.AppendFormat(" auto managed = reinterpret_cast<{0}*>(MCore::Object::Unbox(data));", wrapperName).AppendLine();
header.Append(" ::FreeManaged(*managed);").AppendLine();
}
header.Append(" MCore::GCHandle::Free(*(MGCHandle*)&data);").AppendLine();
header.Append(" }").AppendLine();
header.AppendFormat(" DLLEXPORT USED void FreeManagedArray(MArray* array)").AppendLine();
header.Append(" {").AppendLine();
header.AppendFormat(" {0}* resultPtr = ({0}*)MCore::Array::GetAddress(array);", wrapperName).AppendLine();
header.Append(" const auto length = MCore::Array::GetLength(array);").AppendLine();
header.Append(" for (int32 i = 0; i < length; ++i)").AppendLine();
header.Append(" {").AppendLine();
header.AppendFormat(" ::FreeManaged(*reinterpret_cast<{0}*>(&resultPtr[i]));", wrapperName).AppendLine();
header.Append(" }").AppendLine();
//header.Append(" PLATFORM_DEBUG_BREAK; // FIXME 1").AppendLine();
//header.AppendFormat(" auto managed = MCore::Object::Unbox(data);", wrapperName).AppendLine();
//header.AppendFormat(" ::FreeManaged(*reinterpret_cast<{0}*>(managed));", wrapperName).AppendLine();
header.Append(" MCore::Array::Free(array);").AppendLine();
header.Append(" MCore::GCHandle::Free(*(MGCHandle*)&array);").AppendLine();
header.Append(" }").AppendLine();
header.Append('}').Append(';').AppendLine(); header.Append('}').Append(';').AppendLine();
// Generate converting function native -> managed // Generate converting function native -> managed
@@ -3069,9 +3340,11 @@ namespace Flax.Build.Bindings
continue; continue;
CppNonPodTypesConvertingGeneration = true; CppNonPodTypesConvertingGeneration = true;
var wrapper = GenerateCppWrapperManagedToNative(buildData, fieldInfo.Type, apiType, out _, out _, null, out _); var wrapper = GenerateCppWrapperManagedToNative(buildData, fieldInfo.Type, apiType, out var fieldTypeName, out var fieldType, null, out _);
CppNonPodTypesConvertingGeneration = false; CppNonPodTypesConvertingGeneration = false;
var arrayCountName = $"value.{fieldInfo.Name}.length";
if (fieldInfo.Type.IsArray) if (fieldInfo.Type.IsArray)
{ {
// Fixed-size array needs to unbox every item manually // Fixed-size array needs to unbox every item manually
@@ -3084,7 +3357,7 @@ namespace Flax.Build.Bindings
} }
else else
{ {
wrapper = string.Format(wrapper.Remove(wrapper.Length - 6), string.Format("value.{0}", fieldInfo.Name)); wrapper = string.Format(wrapper.Remove(wrapper.Length - 6), string.Format("value.{0}", fieldInfo.Name), arrayCountName);
header.AppendFormat(" auto tmp{0} = {1};", fieldInfo.Name, wrapper).AppendLine(); header.AppendFormat(" auto tmp{0} = {1};", fieldInfo.Name, wrapper).AppendLine();
header.AppendFormat(" for (int32 i = 0; i < {0} && i < tmp{1}.Count(); i++)", fieldInfo.Type.ArraySize, fieldInfo.Name).AppendLine(); header.AppendFormat(" for (int32 i = 0; i < {0} && i < tmp{1}.Count(); i++)", fieldInfo.Type.ArraySize, fieldInfo.Name).AppendLine();
header.AppendFormat(" result.{0}[i] = tmp{0}[i];", fieldInfo.Name).AppendLine(); header.AppendFormat(" result.{0}[i] = tmp{0}[i];", fieldInfo.Name).AppendLine();
@@ -3095,9 +3368,133 @@ namespace Flax.Build.Bindings
if (string.IsNullOrEmpty(wrapper)) if (string.IsNullOrEmpty(wrapper))
header.AppendFormat(" result.{0} = value.{0};", fieldInfo.Name).AppendLine(); header.AppendFormat(" result.{0} = value.{0};", fieldInfo.Name).AppendLine();
else else
header.AppendFormat(" result.{0} = {1};", fieldInfo.Name, string.Format(wrapper, string.Format("value.{0}", fieldInfo.Name))).AppendLine(); {
var arrayElementType = fieldInfo.Type.GenericArgs != null ? FindApiTypeInfo(buildData, fieldInfo.Type.GenericArgs[0], apiType) : null;
var ispod = (arrayElementType?.IsPod.ToString() ?? "null");
header.Append($" // {fieldInfo.Type.Type}, {fieldType?.FullNameNative}, {fieldType?.Name}, {fieldTypeName}, {ispod}").AppendLine();
if (fieldInfo.Type.IsArrayOrSpan && (arrayElementType?.IsPod ?? false))
{
header.AppendFormat(" result.{0} = {1}; /*kaek1 */", fieldInfo.Name, string.Format(wrapper, $"value.{fieldInfo.Name}.data", $"value.{fieldInfo.Name}.length")).AppendLine();
}
else
{
header.AppendFormat(" result.{0} = {1}; /*kaek2 */", fieldInfo.Name, string.Format(wrapper, $"value.{fieldInfo.Name}", $"value.{fieldInfo.Name}.lengthnot")).AppendLine();
}
//var fieldType = FindApiTypeInfo(buildData, fieldInfo.Type, apiType);
//header.AppendFormat(" result.{0} = {1}; /*kaek2*/", fieldInfo.Name, string.Format(wrapper, $"value.{fieldInfo.Name}")).AppendLine();
}
/*if (wrapper.Contains("::ToNative(")) // FIXME
{
header.Append($" auto __param{fieldInfo.Name}_handle = *(MGCHandle*)&value.{fieldInfo.Name};").AppendLine();
header.Append($" ASSERT((((unsigned long long)__param{fieldInfo.Name}_handle & 0xC000000000000000) >> 62) == 0); // asdf3").AppendLine();
header.Append($" MCore::GCHandle::Free(__param{fieldInfo.Name}_handle);").AppendLine();
}*/
} }
header.Append(" return result;").AppendLine(); header.Append(" return result;").AppendLine();
header.Append('}').AppendLine();
// Generate release function for temporary managed handles
header.AppendLine();
header.AppendFormat("void FreeManaged(const {0}& value)", wrapperName).AppendLine();
header.Append('{').AppendLine();
for (var i = 0; i < fields.Count; i++)
{
var fieldInfo = fields[i];
if (fieldInfo.IsStatic || fieldInfo.IsConstexpr)
continue;
CppNonPodTypesConvertingGeneration = true;
var wrapper = GenerateCppWrapperManagedToNative(buildData, fieldInfo.Type, apiType, out var managedType, out var fieldApiType, null, out _);
CppNonPodTypesConvertingGeneration = false;
if (fieldInfo.Type.IsArray)
{
//header.Append($" fixed_array_type = fixed_array_type; // {fieldInfo.Name} ({managedType}) is fixed array").AppendLine();
}
else if (string.IsNullOrEmpty(wrapper))
{
if (managedType == "bool")
{
}
else
{
}
}
else if (fieldInfo.Type.Type == "String" || managedType == "MString*")
{
if (fieldInfo.Type.Type != "String")
{
}
if (managedType != "MString*")
{
}
header.Append($" MCore::String::Free(value.{fieldInfo.Name});").AppendLine();
}
else if (fieldInfo.Type.Type == "Span")
{
}
else if (fieldInfo.Type.Type == "Array")
{
if (managedType == "MArray*")
{
}
else if (managedType == "Char*" || managedType == "char*")
{
}
else
{
//header.Append($" unhandled_type = unhandled_type; // {fieldInfo.Name} ({managedType}) should be cleaned? // asdfggg56").AppendLine();
}
}
else if (fieldApiType == null)
{
header.Append($" weird_type = weird_type; // {fieldInfo.Name} ({managedType}) is a weird one // okdfiodftg").AppendLine();
}
else if (fieldApiType.IsScriptingObject)
{
header.Append($" // {fieldInfo.Name} is a permanent handle // asdf2").AppendLine();
}
else if (!fieldApiType.IsPod) // FIXMEb
{
if (fieldApiType.IsClass)
{
}
else
{
header.Append($" ::FreeManaged(value.{fieldInfo.Name}); // asdfggg588").AppendLine();
//header.Append($" unhandled_type = unhandled_type; // {fieldInfo.Name} ({managedType}) should be cleaned? // asdfggg588").AppendLine();
}
header.Append($" auto __param{fieldInfo.Name}_handle = *(MGCHandle*)&value.{fieldInfo.Name};").AppendLine();
//header.Append($" ASSERT((((unsigned long long)__param{fieldInfo.Name}_handle & 0xC000000000000000) >> 62) == 0); // asdf3").AppendLine();
header.Append($" if ((((unsigned long long)__param{fieldInfo.Name}_handle & 0xC000000000000000) >> 62) != 0) // asdf3").AppendLine();
header.Append($" __param{fieldInfo.Name}_handle = __param{fieldInfo.Name}_handle;").AppendLine();
header.Append($" MCore::GCHandle::Free(__param{fieldInfo.Name}_handle);").AppendLine();
}
else if (fieldApiType.IsPod)
{
}
else
{
}
}
if (classInfo != null)
{
header.Append($" auto __value_handle = *(MGCHandle*)&value;").AppendLine();
header.Append($" ASSERT((((unsigned long long)__value_handle & 0xC000000000000000) >> 62) == 0); // asdf4").AppendLine();
header.Append($" MCore::GCHandle::Free(__value_handle);").AppendLine();
}
header.Append('}').AppendLine(); header.Append('}').AppendLine();
header.AppendLine("}"); header.AppendLine("}");
} }
@@ -3108,7 +3505,7 @@ namespace Flax.Build.Bindings
header.AppendFormat("struct MConverter<{0}>", fullName).AppendLine(); header.AppendFormat("struct MConverter<{0}>", fullName).AppendLine();
header.Append('{').AppendLine(); header.Append('{').AppendLine();
header.AppendFormat(" static MObject* Box(const {0}& data, const MClass* klass)", fullName).AppendLine(); header.AppendFormat(" DLLEXPORT USED static MObject* Box(const {0}& data, const MClass* klass)", fullName).AppendLine();
header.Append(" {").AppendLine(); header.Append(" {").AppendLine();
header.Append(" MObject* obj = MCore::Object::New(klass);").AppendLine(); header.Append(" MObject* obj = MCore::Object::New(klass);").AppendLine();
for (var i = 0; i < fields.Count; i++) for (var i = 0; i < fields.Count; i++)
@@ -3128,13 +3525,13 @@ namespace Flax.Build.Bindings
header.Append(" return obj;").AppendLine(); header.Append(" return obj;").AppendLine();
header.Append(" }").AppendLine(); header.Append(" }").AppendLine();
header.AppendFormat(" static MObject* Box(const {0}& data)", fullName).AppendLine(); header.AppendFormat(" DLLEXPORT USED static MObject* Box(const {0}& data)", fullName).AppendLine();
header.Append(" {").AppendLine(); header.Append(" {").AppendLine();
header.AppendFormat(" MClass* klass = {0}::TypeInitializer.GetClass();", fullName).AppendLine(); header.AppendFormat(" MClass* klass = {0}::TypeInitializer.GetClass();", fullName).AppendLine();
header.Append(" return Box(data, klass);").AppendLine(); header.Append(" return Box(data, klass);").AppendLine();
header.Append(" }").AppendLine(); header.Append(" }").AppendLine();
header.AppendFormat(" static void Unbox({0}& result, MObject* obj)", fullName).AppendLine(); header.AppendFormat(" DLLEXPORT USED static void Unbox({0}& result, MObject* obj)", fullName).AppendLine();
header.Append(" {").AppendLine(); header.Append(" {").AppendLine();
header.Append(" MClass* klass = MCore::Object::GetClass(obj);").AppendLine(); header.Append(" MClass* klass = MCore::Object::GetClass(obj);").AppendLine();
header.Append(" void* v = nullptr;").AppendLine(); header.Append(" void* v = nullptr;").AppendLine();
@@ -3156,26 +3553,49 @@ namespace Flax.Build.Bindings
} }
header.Append(" }").AppendLine(); header.Append(" }").AppendLine();
header.AppendFormat(" static {0} Unbox(MObject* data)", fullName).AppendLine(); header.AppendFormat(" DLLEXPORT USED static {0} Unbox(MObject* data)", fullName).AppendLine();
header.Append(" {").AppendLine(); header.Append(" {").AppendLine();
header.AppendFormat(" {0} result;", fullName).AppendLine(); header.AppendFormat(" {0} result;", fullName).AppendLine();
header.Append(" Unbox(result, data);").AppendLine(); header.Append(" Unbox(result, data);").AppendLine();
header.Append(" return result;").AppendLine(); header.Append(" return result;").AppendLine();
header.Append(" }").AppendLine(); header.Append(" }").AppendLine();
header.AppendFormat(" void ToManagedArray(MArray* result, const Span<{0}>& data)", fullName).AppendLine(); header.AppendFormat(" DLLEXPORT USED void ToManagedArray(MArray* result, const Span<{0}>& data)", fullName).AppendLine();
header.Append(" {").AppendLine(); header.Append(" {").AppendLine();
header.Append(" for (int32 i = 0; i < data.Length(); i++)").AppendLine(); header.Append(" for (int32 i = 0; i < data.Length(); i++)").AppendLine();
header.Append(" MCore::GC::WriteArrayRef(result, Box(data[i]), i);").AppendLine(); header.Append(" MCore::GC::WriteArrayRef(result, Box(data[i]), i);").AppendLine();
header.Append(" }").AppendLine(); header.Append(" }").AppendLine();
header.AppendFormat(" void ToNativeArray(Span<{0}>& result, const MArray* data)", fullName).AppendLine(); header.AppendFormat(" DLLEXPORT USED void ToNativeArray(Span<{0}>& result, const MArray* data)", fullName).AppendLine();
header.Append(" {").AppendLine(); header.Append(" {").AppendLine();
header.Append(" MObject** dataPtr = (MObject**)MCore::Array::GetAddress(data);").AppendLine(); header.Append(" MObject** dataPtr = (MObject**)MCore::Array::GetAddress(data);").AppendLine();
header.Append(" for (int32 i = 0; i < result.Length(); i++)").AppendLine(); header.Append(" for (int32 i = 0; i < result.Length(); i++)").AppendLine();
header.AppendFormat(" Unbox(result[i], dataPtr[i]);", fullName).AppendLine(); header.AppendFormat(" Unbox(result[i], dataPtr[i]);", fullName).AppendLine();
header.Append(" }").AppendLine(); header.Append(" }").AppendLine();
header.AppendFormat(" DLLEXPORT USED void FreeManaged(MObject* data)").AppendLine();
header.Append(" {").AppendLine();
header.Append(" PLATFORM_DEBUG_BREAK; // FIXME 2b").AppendLine();
header.Append(" MCore::GCHandle::Free(*(MGCHandle*)&data);").AppendLine();
header.Append(" }").AppendLine();
header.AppendFormat(" DLLEXPORT USED void FreeManagedArray(MArray* array)").AppendLine();
header.Append(" {").AppendLine();
header.Append(" PLATFORM_DEBUG_BREAK; // FIXME 2").AppendLine();
header.Append(" MObject** resultPtr = (MObject**)MCore::Array::GetAddress(array);").AppendLine();
header.Append(" const auto length = MCore::Array::GetLength(array);").AppendLine();
header.Append(" for (int32 i = 0; i < length; ++i)").AppendLine();
header.Append(" {").AppendLine();
header.Append(" FreeManaged(resultPtr[i]);").AppendLine();
//header.AppendFormat(" MCore::GCHandle::Free(*(MGCHandle*)&resultPtr[i]);", wrapperName).AppendLine();
header.Append(" }").AppendLine();
//header.Append(" PLATFORM_DEBUG_BREAK; // FIXME 1").AppendLine();
//header.AppendFormat(" auto managed = MCore::Object::Unbox(data);", wrapperName).AppendLine();
//header.AppendFormat(" ::FreeManaged(*reinterpret_cast<{0}*>(managed));", wrapperName).AppendLine();
header.Append(" MCore::Array::Free(array);").AppendLine();
header.Append(" MCore::GCHandle::Free(*(MGCHandle*)&array);").AppendLine();
header.Append(" }").AppendLine();
header.Append('}').Append(';').AppendLine(); header.Append('}').Append(';').AppendLine();
} }
} }

View File

@@ -18,30 +18,42 @@ namespace Flax.Build.Bindings
public bool IsRef; public bool IsRef;
public bool IsMoveRef; public bool IsMoveRef;
public bool IsPtr; public bool IsPtr;
/// <summary>
/// Is this a fixed-length type of array.
/// </summary>
public bool IsArray; public bool IsArray;
public bool IsBitField; public bool IsBitField;
public int ArraySize; public int ArraySize;
public int BitSize; public int BitSize;
public List<TypeInfo> GenericArgs; public List<TypeInfo> GenericArgs;
/// <summary> /// <summary>
/// Gets a value indicating whether this type is void. /// Is this a void type.
/// </summary> /// </summary>
public bool IsVoid => Type == "void" && !IsPtr; public bool IsVoid => Type == "void" && !IsPtr;
/// <summary> /// <summary>
/// Gets a value indicating whether this type is constant reference to a value. /// Is this a constant reference to a value.
/// </summary> /// </summary>
public bool IsConstRef => IsRef && IsConst; public bool IsConstRef => IsRef && IsConst;
/// <summary> /// <summary>
/// Gets a value indicating whether this type is a reference to another object. /// Is this a reference to another FlaxEngine.Object.
/// </summary> /// </summary>
public bool IsObjectRef => (Type == "ScriptingObjectReference" || public bool IsObjectRef => Type is "ScriptingObjectReference"
Type == "AssetReference" || or "AssetReference"
Type == "WeakAssetReference" || or "WeakAssetReference"
Type == "SoftAssetReference" || or "SoftAssetReference"
Type == "SoftObjectReference") && GenericArgs != null; or "SoftObjectReference"
&& GenericArgs != null;
/// <summary>
/// Is this an inbuilt container type.
/// </summary>
public bool IsArrayOrSpan => Type is "Array" or "Span" or "DataContainer" or "BytesContainer";
public TypeInfo() public TypeInfo()
{ {