From 8c4771a738e9723815920612e11462f66c2bba16 Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Sun, 18 Dec 2022 12:29:46 +0200 Subject: [PATCH 1/4] Fix SEH exceptions not handled properly when debugger is attached --- Source/Engine/Platform/Windows/WindowsPlatform.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Platform/Windows/WindowsPlatform.cpp b/Source/Engine/Platform/Windows/WindowsPlatform.cpp index 1e4337016..5a1604d97 100644 --- a/Source/Engine/Platform/Windows/WindowsPlatform.cpp +++ b/Source/Engine/Platform/Windows/WindowsPlatform.cpp @@ -308,7 +308,7 @@ LONG CALLBACK SehExceptionHandler(EXCEPTION_POINTERS* ep) // Pause if debugging if (Platform::IsDebuggerPresent()) { - PLATFORM_DEBUG_BREAK; + return EXCEPTION_CONTINUE_SEARCH; } // Crash engine From 15af6502a00bcee3caa91680864204a7aa3862b0 Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Tue, 20 Dec 2022 22:31:11 +0200 Subject: [PATCH 2/4] Log error whenever build tool fails to run --- Source/Editor/Scripting/ScriptsBuilder.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Source/Editor/Scripting/ScriptsBuilder.cpp b/Source/Editor/Scripting/ScriptsBuilder.cpp index 1f119f9af..9c6554e9e 100644 --- a/Source/Editor/Scripting/ScriptsBuilder.cpp +++ b/Source/Editor/Scripting/ScriptsBuilder.cpp @@ -246,6 +246,8 @@ bool ScriptsBuilder::RunBuildTool(const StringView& args, const StringView& work // Call build tool const int32 result = Platform::RunProcess(StringView(*cmdLine, cmdLine.Length()), workingDir); + if (result != 0) + LOG(Error, "Failed to run build tool, result: {0:x}", (uint32)result); return result != 0; } From 55747edaae97a40efd421565d6b02a2fcd0a9cca Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Tue, 20 Dec 2022 22:39:16 +0200 Subject: [PATCH 3/4] Cleanup --- Source/Engine/Scripting/DotNet/CoreCLR.cpp | 3 - Source/Engine/Scripting/DotNet/CoreCLR.h | 19 +- Source/Engine/Scripting/DotNet/MonoApi.cpp | 286 ++++++++++-------- Source/Engine/Scripting/ManagedCLR/MCore.cpp | 8 +- Source/Engine/Utilities/Utils.cs | 3 + .../Bindings/BindingsGenerator.CSharp.cs | 54 ++-- 6 files changed, 206 insertions(+), 167 deletions(-) diff --git a/Source/Engine/Scripting/DotNet/CoreCLR.cpp b/Source/Engine/Scripting/DotNet/CoreCLR.cpp index 4d7d53052..fe3983b0f 100644 --- a/Source/Engine/Scripting/DotNet/CoreCLR.cpp +++ b/Source/Engine/Scripting/DotNet/CoreCLR.cpp @@ -42,9 +42,6 @@ bool CoreCLR::LoadHostfxr(const String& library_path_) { const FLAX_CORECLR_STRING& library_path = FLAX_CORECLR_STRING(library_path_); - Platform::SetEnvironmentVariable(TEXT("DOTNET_TieredPGO"), TEXT("1")); - Platform::SetEnvironmentVariable(TEXT("DOTNET_TC_QuickJitForLoops"), TEXT("1")); - Platform::SetEnvironmentVariable(TEXT("DOTNET_ReadyToRun"), TEXT("0")); char_t hostfxrPath[1024]; size_t hostfxrPathSize = sizeof(hostfxrPath) / sizeof(char_t); diff --git a/Source/Engine/Scripting/DotNet/CoreCLR.h b/Source/Engine/Scripting/DotNet/CoreCLR.h index b2dc29cbf..031279488 100644 --- a/Source/Engine/Scripting/DotNet/CoreCLR.h +++ b/Source/Engine/Scripting/DotNet/CoreCLR.h @@ -22,22 +22,29 @@ public: static bool LoadHostfxr(const String& library_path); static bool InitHostfxr(const String& config_path, const String& library_path); + /// + /// Returns the function pointer to the managed static method in NativeInterop class. + /// static void* GetStaticMethodPointer(const String& methodName); + /// + /// Calls the managed static method in NativeInterop class with given parameters. + /// template - static RetType CallStaticMethodInternal(const String& methodName, Args... args) + static inline RetType CallStaticMethodByName(const String& methodName, Args... args) { typedef RetType(CORECLR_DELEGATE_CALLTYPE* fun)(Args...); - fun function = (fun)GetStaticMethodPointer(methodName); - return function(args...); + return ((fun)GetStaticMethodPointer(methodName))(args...); } + /// + /// Calls the managed static method with given parameters. + /// template - static RetType CallStaticMethodInternalPointer(void* funPtr, Args... args) + static inline RetType CallStaticMethod(void* methodPtr, Args... args) { typedef RetType(CORECLR_DELEGATE_CALLTYPE* fun)(Args...); - fun function = (fun)funPtr; - return function(args...); + return ((fun)methodPtr)(args...); } static const char* GetClassFullname(void* klass); diff --git a/Source/Engine/Scripting/DotNet/MonoApi.cpp b/Source/Engine/Scripting/DotNet/MonoApi.cpp index 8409bd72a..05c03f17c 100644 --- a/Source/Engine/Scripting/DotNet/MonoApi.cpp +++ b/Source/Engine/Scripting/DotNet/MonoApi.cpp @@ -82,14 +82,15 @@ private: public: CoreCLRAssembly(void* assemblyHandle, const char* name, const char* fullname) { + static void* GetManagedClassesPtr = CoreCLR::GetStaticMethodPointer(TEXT("GetManagedClasses")); + _assemblyHandle = assemblyHandle; _name = name; _fullname = fullname; ManagedClass* managedClasses; int classCount; - - CoreCLR::CallStaticMethodInternal(TEXT("GetManagedClasses"), _assemblyHandle, &managedClasses, &classCount); + CoreCLR::CallStaticMethod(GetManagedClassesPtr, _assemblyHandle, &managedClasses, &classCount); for (int i = 0; i < classCount; i++) { CoreCLRClass* mci = New(managedClasses[i].typeHandle, StringAnsi(managedClasses[i].name), StringAnsi(managedClasses[i].fullname), StringAnsi(managedClasses[i].namespace_), managedClasses[i].typeAttributes, this); @@ -113,22 +114,22 @@ public: assemblyHandles.Remove(_assemblyHandle); } - void* GetHandle() + void* GetHandle() const { return _assemblyHandle; } - const StringAnsi& GetName() + const StringAnsi& GetName() const { return _name; } - const StringAnsi& GetFullname() + const StringAnsi& GetFullname() const { return _fullname; } - Array GetClasses() + const Array& GetClasses() const { return _classes; } @@ -170,11 +171,6 @@ public: ~CoreCLRClass() { - for (auto method : _methods) - { - //int rem = monoMethods.RemoveValue(method); - //ASSERT(rem > 0) - } _methods.ClearDelete(); _fields.ClearDelete(); _attributes.ClearDelete(); @@ -184,12 +180,12 @@ public: classHandles.Remove(_typeHandle); } - uint32 GetAttributes() + uint32 GetAttributes() const { return _typeAttributes; } - uint32 GetTypeToken() + uint32 GetTypeToken() const { return _typeToken; } @@ -199,46 +195,47 @@ public: if (_size != 0) return _size; - uint32 dummy; - _size = mono_class_value_size((MonoClass*)this, &dummy); + uint32 align; + _size = mono_class_value_size((MonoClass*)this, &align); return _size; } - const StringAnsi& GetName() + const StringAnsi& GetName() const { return _name; } - const StringAnsi& GetFullname() + const StringAnsi& GetFullname() const { - return _fullname; // FIXME: this should probably return the decorated C# name for generic types (foo) and not the IL-name (foo`1[[T) + return _fullname; } - const StringAnsi& GetNamespace() + const StringAnsi& GetNamespace() const { return _namespace; } - void* GetTypeHandle() + void* GetTypeHandle() const { return _typeHandle; } - CoreCLRAssembly* GetAssembly() + const CoreCLRAssembly* GetAssembly() const { return _image; } - Array GetMethods() + const Array& GetMethods() { if (_cachedMethods) return _methods; + + static void* GetClassMethodsPtr = CoreCLR::GetStaticMethodPointer(TEXT("GetClassMethods")); ClassMethod* foundMethods; int numMethods; - - CoreCLR::CallStaticMethodInternal(TEXT("GetClassMethods"), _typeHandle, &foundMethods, &numMethods); + CoreCLR::CallStaticMethod(GetClassMethodsPtr, _typeHandle, &foundMethods, &numMethods); for (int i = 0; i < numMethods; i++) { CoreCLRMethod* method = New(StringAnsi(foundMethods[i].name), foundMethods[i].numParameters, foundMethods[i].handle, foundMethods[i].methodAttributes, this); @@ -252,15 +249,16 @@ public: return _methods; } - Array GetFields() + const Array& GetFields() { if (_cachedFields) return _fields; + static void* GetClassFieldsPtr = CoreCLR::GetStaticMethodPointer(TEXT("GetClassFields")); + ClassField* foundFields; int numFields; - - CoreCLR::CallStaticMethodInternal(TEXT("GetClassFields"), _typeHandle, &foundFields, &numFields); + CoreCLR::CallStaticMethod(GetClassFieldsPtr, _typeHandle, &foundFields, &numFields); for (int i = 0; i < numFields; i++) { CoreCLRField* field = New(StringAnsi(foundFields[i].name), foundFields[i].fieldHandle, foundFields[i].fieldType, foundFields[i].fieldAttributes, this); @@ -274,15 +272,16 @@ public: return _fields; } - Array GetProperties() + const Array& GetProperties() { if (_cachedProperties) return _properties; + static void* GetClassPropertiesPtr = CoreCLR::GetStaticMethodPointer(TEXT("GetClassProperties")); + ClassProperty* foundProperties; int numProperties; - - CoreCLR::CallStaticMethodInternal(TEXT("GetClassProperties"), _typeHandle, &foundProperties, &numProperties); + CoreCLR::CallStaticMethod(GetClassPropertiesPtr, _typeHandle, &foundProperties, &numProperties); for (int i = 0; i < numProperties; i++) { CoreCLRProperty* prop = New(StringAnsi(foundProperties[i].name), foundProperties[i].getterHandle, foundProperties[i].setterHandle, foundProperties[i].getterFlags, foundProperties[i].setterFlags, this); @@ -296,15 +295,16 @@ public: return _properties; } - Array GetCustomAttributes() + const Array& GetCustomAttributes() { if (_cachedAttributes) return _attributes; + static void* GetClassAttributesPtr = CoreCLR::GetStaticMethodPointer(TEXT("GetClassAttributes")); + ClassAttribute* foundAttributes; int numAttributes; - - CoreCLR::CallStaticMethodInternal(TEXT("GetClassAttributes"), _typeHandle, &foundAttributes, &numAttributes); + CoreCLR::CallStaticMethod(GetClassAttributesPtr, _typeHandle, &foundAttributes, &numAttributes); for (int i = 0; i < numAttributes; i++) { CoreCLRClass* attributeClass = GetClass(foundAttributes[i].attributeTypeHandle); @@ -319,15 +319,16 @@ public: return _attributes; } - Array GetInterfaces() + const Array& GetInterfaces() { if (_cachedInterfaces) return _interfaces; + static void* GetClassInterfacesPtr = CoreCLR::GetStaticMethodPointer(TEXT("GetClassInterfaces")); + void** foundInterfaces; int numInterfaces; - - CoreCLR::CallStaticMethodInternal(TEXT("GetClassInterfaces"), _typeHandle, &foundInterfaces, &numInterfaces); + CoreCLR::CallStaticMethod(GetClassInterfacesPtr, _typeHandle, &foundInterfaces, &numInterfaces); for (int i = 0; i < numInterfaces; i++) { CoreCLRClass* interfaceClass = classHandles[foundInterfaces[i]]; @@ -358,32 +359,32 @@ public: { } - const StringAnsi& GetName() + const StringAnsi& GetName() const { return _name; } - CoreCLRClass* GetClass() + const CoreCLRClass* GetClass() const { return _class; } - uint32 GetAttributes() + uint32 GetAttributes() const { return _methodAttributes; } - int GetNumParameters() + int GetNumParameters() const { return _numParams; } - void* GetMethodHandle() + void* GetMethodHandle() const { return _methodHandle; } - Array GetParameterTypes() + const Array& GetParameterTypes() { if (!_cachedParameters) CacheParameters(); @@ -397,19 +398,17 @@ public: return _returnType; } +private: void CacheParameters() { - _returnType = CoreCLR::CallStaticMethodInternal(TEXT("GetMethodReturnType"), _methodHandle); + static void* GetMethodReturnTypePtr = CoreCLR::GetStaticMethodPointer(TEXT("GetMethodReturnType")); + static void* GetMethodParameterTypesPtr = CoreCLR::GetStaticMethodPointer(TEXT("GetMethodParameterTypes")); + + _returnType = CoreCLR::CallStaticMethod(GetMethodReturnTypePtr, _methodHandle); void** parameterTypeHandles; - CoreCLR::CallStaticMethodInternal(TEXT("GetMethodParameterTypes"), _methodHandle, ¶meterTypeHandles); - - _parameterTypes.SetCapacity(_numParams, false); - - for (int i = 0; i < _numParams; i++) - { - _parameterTypes.Add(parameterTypeHandles[i]); - } + CoreCLR::CallStaticMethod(GetMethodParameterTypesPtr, _methodHandle, ¶meterTypeHandles); + _parameterTypes.Set(parameterTypeHandles, _numParams); CoreCLR::Free(parameterTypeHandles); _cachedParameters = true; @@ -431,27 +430,27 @@ public: { } - const StringAnsi& GetName() + const StringAnsi& GetName() const { return _name; } - void* GetType() + void* GetType() const { return _fieldType; } - CoreCLRClass* GetClass() + const CoreCLRClass* GetClass() const { return _class; } - uint32 GetAttributes() + uint32 GetAttributes() const { return _fieldAttributes; } - void* GetHandle() + void* GetHandle() const { return _fieldHandle; } @@ -475,22 +474,22 @@ public: _setMethod = New(StringAnsi(_name + "Set"), 1, setter, setterFlags, klass); } - const StringAnsi& GetName() + const StringAnsi& GetName() const { return _name; } - CoreCLRClass* GetClass() + const CoreCLRClass* GetClass() const { return _class; } - CoreCLRMethod* GetGetMethod() + const CoreCLRMethod* GetGetMethod() const { return _getMethod; } - CoreCLRMethod* GetSetMethod() + const CoreCLRMethod* GetSetMethod() const { return _setMethod; } @@ -510,12 +509,12 @@ public: { } - void* GetHandle() + void* GetHandle() const { return _handle; } - CoreCLRClass* GetClass() + const CoreCLRClass* GetClass() const { return _attributeClass; } @@ -542,16 +541,18 @@ CoreCLRClass* GetOrCreateClass(void* type) CoreCLRClass* klass; if (!classHandles.TryGet(type, klass)) { + static void* GetManagedClassFromTypePtr = CoreCLR::GetStaticMethodPointer(TEXT("GetManagedClassFromType")); + ManagedClass classInfo; void* assemblyHandle; - CoreCLR::CallStaticMethodInternal(TEXT("GetManagedClassFromType"), type, &classInfo, &assemblyHandle); + CoreCLR::CallStaticMethod(GetManagedClassFromTypePtr, type, &classInfo, &assemblyHandle); CoreCLRAssembly* image = GetAssembly(assemblyHandle); klass = New(classInfo.typeHandle, StringAnsi(classInfo.name), StringAnsi(classInfo.fullname), StringAnsi(classInfo.namespace_), classInfo.typeAttributes, image); if (image != nullptr) image->AddClass(klass); if (type != classInfo.typeHandle) - CoreCLR::CallStaticMethodInternal(TEXT("GetManagedClassFromType"), type, &classInfo); + CoreCLR::CallStaticMethod(GetManagedClassFromTypePtr, type, &classInfo); classHandles.Add(classInfo.typeHandle, klass); CoreCLR::Free((void*)classInfo.name); @@ -564,12 +565,14 @@ CoreCLRClass* GetOrCreateClass(void* type) gchandle CoreCLR::NewGCHandle(void* obj, bool pinned) { - return (gchandle)CoreCLR::CallStaticMethodInternal(TEXT("NewGCHandle"), obj, pinned); + static void* NewGCHandlePtr = CoreCLR::GetStaticMethodPointer(TEXT("NewGCHandle")); + return (gchandle)CoreCLR::CallStaticMethod(NewGCHandlePtr, obj, pinned); } gchandle CoreCLR::NewGCHandleWeakref(void* obj, bool track_resurrection) { - return (gchandle)CoreCLR::CallStaticMethodInternal(TEXT("NewGCHandleWeakref"), obj, track_resurrection); + static void* NewGCHandleWeakrefPtr = CoreCLR::GetStaticMethodPointer(TEXT("NewGCHandleWeakref")); + return (gchandle)CoreCLR::CallStaticMethod(NewGCHandleWeakrefPtr, obj, track_resurrection); } void* CoreCLR::GetGCHandleTarget(const gchandle& gchandle) @@ -579,7 +582,8 @@ void* CoreCLR::GetGCHandleTarget(const gchandle& gchandle) void CoreCLR::FreeGCHandle(const gchandle& gchandle) { - CoreCLR::CallStaticMethodInternal(TEXT("FreeGCHandle"), (void*)gchandle); + static void* FreeGCHandlePtr = CoreCLR::GetStaticMethodPointer(TEXT("FreeGCHandle")); + CoreCLR::CallStaticMethod(FreeGCHandlePtr, (void*)gchandle); } const char* CoreCLR::GetClassFullname(void* klass) @@ -597,7 +601,8 @@ bool CoreCLR::HasCustomAttribute(void* klass) } void* CoreCLR::GetCustomAttribute(void* klass, void* attribClass) { - return CoreCLR::CallStaticMethodInternal(TEXT("GetCustomAttribute"), ((CoreCLRClass*)klass)->GetTypeHandle(), ((CoreCLRClass*)attribClass)->GetTypeHandle()); + static void* GetCustomAttributePtr = CoreCLR::GetStaticMethodPointer(TEXT("GetCustomAttribute")); + return CoreCLR::CallStaticMethod(GetCustomAttributePtr, ((CoreCLRClass*)klass)->GetTypeHandle(), ((CoreCLRClass*)attribClass)->GetTypeHandle()); } Array CoreCLR::GetCustomAttributes(void* klass) { @@ -646,66 +651,78 @@ MONO_API MONO_RT_EXTERNAL_ONLY void mono_add_internal_call(const char* name, con MONO_API mono_unichar2* mono_string_chars(MonoString* s) { - _MonoString* str = (_MonoString*)CoreCLR::CallStaticMethodInternal(TEXT("GetStringPointer"), s); + static void* GetStringPointerPtr = CoreCLR::GetStaticMethodPointer(TEXT("GetStringPointer")); + _MonoString* str = (_MonoString*)CoreCLR::CallStaticMethod(GetStringPointerPtr, s); return str->chars; } MONO_API int mono_string_length(MonoString* s) { - _MonoString* str = (_MonoString*)CoreCLR::CallStaticMethodInternal(TEXT("GetStringPointer"), s); + static void* GetStringPointerPtr = CoreCLR::GetStaticMethodPointer(TEXT("GetStringPointer")); + _MonoString* str = (_MonoString*)CoreCLR::CallStaticMethod(GetStringPointerPtr, s); return str->length; } MONO_API MONO_RT_EXTERNAL_ONLY MonoObject* mono_object_new(MonoDomain* domain, MonoClass* klass) { - return (MonoObject*)CoreCLR::CallStaticMethodInternal(TEXT("NewObject"), ((CoreCLRClass*)klass)->GetTypeHandle()); + static void* NewObjectPtr = CoreCLR::GetStaticMethodPointer(TEXT("NewObject")); + return (MonoObject*)CoreCLR::CallStaticMethod(NewObjectPtr, ((CoreCLRClass*)klass)->GetTypeHandle()); } MONO_API MONO_RT_EXTERNAL_ONLY MonoArray* mono_array_new(MonoDomain* domain, MonoClass* eclass, uintptr_t n) { - return (MonoArray*)CoreCLR::CallStaticMethodInternal(TEXT("NewArray"), ((CoreCLRClass*)eclass)->GetTypeHandle(), n); + static void* NewArrayPtr = CoreCLR::GetStaticMethodPointer(TEXT("NewArray")); + return (MonoArray*)CoreCLR::CallStaticMethod(NewArrayPtr, ((CoreCLRClass*)eclass)->GetTypeHandle(), n); } MONO_API char* mono_array_addr_with_size(MonoArray* array, int size, uintptr_t idx) { - return (char*)CoreCLR::CallStaticMethodInternal(TEXT("GetArrayPointerToElement"), array, size, (int)idx); + static void* GetArrayPointerToElementPtr = CoreCLR::GetStaticMethodPointer(TEXT("GetArrayPointerToElement")); + return (char*)CoreCLR::CallStaticMethod(GetArrayPointerToElementPtr, array, size, (int)idx); } MONO_API uintptr_t mono_array_length(MonoArray* array) { - return CoreCLR::CallStaticMethodInternal(TEXT("GetArrayLength"), array); + static void* GetArrayLengthPtr = CoreCLR::GetStaticMethodPointer(TEXT("GetArrayLength")); + return CoreCLR::CallStaticMethod(GetArrayLengthPtr, array); } MONO_API MonoString* mono_string_empty(MonoDomain* domain) { - return (MonoString*)CoreCLR::CallStaticMethodInternal(TEXT("GetStringEmpty")); + static void* GetStringEmptyPtr = CoreCLR::GetStaticMethodPointer(TEXT("GetStringEmpty")); + return (MonoString*)CoreCLR::CallStaticMethod(GetStringEmptyPtr); } MONO_API MONO_RT_EXTERNAL_ONLY MonoString* mono_string_new_utf16(MonoDomain* domain, const mono_unichar2* text, int32_t len) { - return (MonoString*)CoreCLR::CallStaticMethodInternal(TEXT("NewStringUTF16"), text, len); + static void* NewStringUTF16Ptr = CoreCLR::GetStaticMethodPointer(TEXT("NewStringUTF16")); + return (MonoString*)CoreCLR::CallStaticMethod(NewStringUTF16Ptr, text, len); } MONO_API MONO_RT_EXTERNAL_ONLY MonoString* mono_string_new(MonoDomain* domain, const char* text) { - return (MonoString*)CoreCLR::CallStaticMethodInternal(TEXT("NewString"), text); + static void* NewStringPtr = CoreCLR::GetStaticMethodPointer(TEXT("NewString")); + return (MonoString*)CoreCLR::CallStaticMethod(NewStringPtr, text); } MONO_API MONO_RT_EXTERNAL_ONLY MonoString* mono_string_new_len(MonoDomain* domain, const char* text, unsigned int length) { - return (MonoString*)CoreCLR::CallStaticMethodInternal(TEXT("NewStringLength"), text, length); + static void* NewStringLengthPtr = CoreCLR::GetStaticMethodPointer(TEXT("NewStringLength")); + return (MonoString*)CoreCLR::CallStaticMethod(NewStringLengthPtr, text, length); } MONO_API MONO_RT_EXTERNAL_ONLY char* mono_string_to_utf8(MonoString* string_obj) { - Char* strw = string_obj != nullptr ? (Char*)mono_string_chars(string_obj) : nullptr; - auto len = string_obj != nullptr ? mono_string_length(string_obj) : 0; - ASSERT(len >= 0) - char* stra = (char*)CoreCLR::Allocate(sizeof(char) * (len + 1)); - StringUtils::ConvertUTF162UTF8(strw, stra, len, len); - stra[len] = 0; + static void* GetStringPointerPtr = CoreCLR::GetStaticMethodPointer(TEXT("GetStringPointer")); + _MonoString* monoString = (_MonoString*)CoreCLR::CallStaticMethod(GetStringPointerPtr, string_obj); - return stra; + auto len = monoString->length; + ASSERT(len >= 0) + char* str = (char*)CoreCLR::Allocate(sizeof(char) * (len + 1)); + StringUtils::ConvertUTF162UTF8((Char*)monoString->chars, str, len, len); + str[len] = 0; + + return str; } MONO_API MONO_RT_EXTERNAL_ONLY MonoString* mono_object_to_string(MonoObject* obj, MonoObject** exc) @@ -720,7 +737,8 @@ MONO_API int mono_object_hash(MonoObject* obj) MONO_API MONO_RT_EXTERNAL_ONLY MonoObject* mono_value_box(MonoDomain* domain, MonoClass* klass, void* val) { - return (MonoObject*)CoreCLR::CallStaticMethodInternal(TEXT("BoxValue"), ((CoreCLRClass*)klass)->GetTypeHandle(), val); + static void* BoxValuePtr = CoreCLR::GetStaticMethodPointer(TEXT("BoxValue")); + return (MonoObject*)CoreCLR::CallStaticMethod(BoxValuePtr, ((CoreCLRClass*)klass)->GetTypeHandle(), val); } MONO_API void mono_value_copy(void* dest, /*const*/ void* src, MonoClass* klass) @@ -731,7 +749,8 @@ MONO_API void mono_value_copy(void* dest, /*const*/ void* src, MonoClass* klass) MONO_API MONO_RT_EXTERNAL_ONLY MonoClass* mono_object_get_class(MonoObject* obj) { - void* classHandle = CoreCLR::CallStaticMethodInternal(TEXT("GetObjectType"), obj); + static void* GetObjectTypePtr = CoreCLR::GetStaticMethodPointer(TEXT("GetObjectType")); + void* classHandle = CoreCLR::CallStaticMethod(GetObjectTypePtr, obj); CoreCLRClass* mi = GetOrCreateClass((void*)classHandle); @@ -741,17 +760,20 @@ MONO_API MONO_RT_EXTERNAL_ONLY MonoClass* mono_object_get_class(MonoObject* obj) MONO_API void* mono_object_unbox(MonoObject* obj) { - return CoreCLR::CallStaticMethodInternal(TEXT("UnboxValue"), obj); + static void* UnboxValuePtr = CoreCLR::GetStaticMethodPointer(TEXT("UnboxValue")); + return CoreCLR::CallStaticMethod(UnboxValuePtr, obj); } MONO_API MONO_RT_EXTERNAL_ONLY void mono_raise_exception(MonoException* ex) { - CoreCLR::CallStaticMethodInternal(TEXT("RaiseException"), ex); + static void* RaiseExceptionPtr = CoreCLR::GetStaticMethodPointer(TEXT("RaiseException")); + CoreCLR::CallStaticMethod(RaiseExceptionPtr, ex); } MONO_API MONO_RT_EXTERNAL_ONLY void mono_runtime_object_init(MonoObject* this_obj) { - CoreCLR::CallStaticMethodInternal(TEXT("ObjectInit"), this_obj); + static void* ObjectInitPtr = CoreCLR::GetStaticMethodPointer(TEXT("ObjectInit")); + CoreCLR::CallStaticMethod(ObjectInitPtr, this_obj); } MONO_API MonoMethod* mono_object_get_virtual_method(MonoObject* obj, MonoMethod* method) @@ -763,29 +785,32 @@ MONO_API MONO_RT_EXTERNAL_ONLY MonoObject* mono_runtime_invoke(MonoMethod* metho { CoreCLRMethod* mi = (CoreCLRMethod*)method; void* methodPtr = mi->GetMethodHandle(); - ASSERT(methodPtr != nullptr) + ASSERT(methodPtr != nullptr); static void* InvokeMethodPtr = CoreCLR::GetStaticMethodPointer(TEXT("InvokeMethod")); - return (MonoObject*)CoreCLR::CallStaticMethodInternalPointer(InvokeMethodPtr, obj, methodPtr, params, exc); + return (MonoObject*)CoreCLR::CallStaticMethod(InvokeMethodPtr, obj, methodPtr, params, exc); } MONO_API MONO_RT_EXTERNAL_ONLY void* mono_method_get_unmanaged_thunk(MonoMethod* method) { CoreCLRMethod* mi = (CoreCLRMethod*)method; void* methodPtr = mi->GetMethodHandle(); - ASSERT(methodPtr != nullptr) + ASSERT(methodPtr != nullptr); - return CoreCLR::CallStaticMethodInternal(TEXT("GetMethodUnmanagedFunctionPointer"), methodPtr); + static void* GetMethodUnmanagedFunctionPointerPtr = CoreCLR::GetStaticMethodPointer(TEXT("GetMethodUnmanagedFunctionPointer")); + return CoreCLR::CallStaticMethod(GetMethodUnmanagedFunctionPointerPtr, methodPtr); } MONO_API void mono_field_set_value(MonoObject* obj, MonoClassField* field, void* value) { - CoreCLR::CallStaticMethodInternal(TEXT("FieldSetValue"), obj, ((CoreCLRField*)field)->GetHandle(), value); + static void* FieldSetValuePtr = CoreCLR::GetStaticMethodPointer(TEXT("FieldSetValue")); + CoreCLR::CallStaticMethod(FieldSetValuePtr, obj, ((CoreCLRField*)field)->GetHandle(), value); } MONO_API void mono_field_get_value(MonoObject* obj, MonoClassField* field, void* value) { - CoreCLR::CallStaticMethodInternal(TEXT("FieldGetValue"), obj, ((CoreCLRField*)field)->GetHandle(), value); + static void* FieldGetValuePtr = CoreCLR::GetStaticMethodPointer(TEXT("FieldGetValue")); + CoreCLR::CallStaticMethod(FieldGetValuePtr, obj, ((CoreCLRField*)field)->GetHandle(), value); } MONO_API MONO_RT_EXTERNAL_ONLY MonoObject* mono_field_get_value_object(MonoDomain* domain, MonoClassField* field, MonoObject* obj) @@ -809,7 +834,8 @@ MONO_API void mono_gc_wbarrier_set_field(MonoObject* obj, void* field_ptr, MonoO } MONO_API void mono_gc_wbarrier_set_arrayref(MonoArray* arr, void* slot_ptr, MonoObject* value) { - CoreCLR::CallStaticMethodInternal(TEXT("SetArrayValueReference"), arr, slot_ptr, value); + static void* SetArrayValueReferencePtr = CoreCLR::GetStaticMethodPointer(TEXT("SetArrayValueReference")); + CoreCLR::CallStaticMethod(SetArrayValueReferencePtr, arr, slot_ptr, value); } MONO_API void mono_gc_wbarrier_generic_store(void* ptr, MonoObject* value) { @@ -844,7 +870,8 @@ MONO_API MonoAssembly* mono_domain_assembly_open(MonoDomain* domain, const char* { const char* name; const char* fullname; - void* assemblyHandle = CoreCLR::CallStaticMethodInternal(TEXT("LoadAssemblyFromPath"), path, &name, &fullname); + static void* LoadAssemblyFromPathPtr = CoreCLR::GetStaticMethodPointer(TEXT("LoadAssemblyFromPath")); + void* assemblyHandle = CoreCLR::CallStaticMethod(LoadAssemblyFromPathPtr, path, &name, &fullname); CoreCLRAssembly* assembly = New(assemblyHandle, name, fullname); CoreCLR::Free((void*)name); @@ -861,7 +888,8 @@ MONO_API MonoImage* mono_get_corlib(void) { const char* name; const char* fullname; - void* assemblyHandle = CoreCLR::CallStaticMethodInternal(TEXT("GetAssemblyByName"), "System.Private.CoreLib", &name, &fullname); + static void* GetAssemblyByNamePtr = CoreCLR::GetStaticMethodPointer(TEXT("GetAssemblyByName")); + void* assemblyHandle = CoreCLR::CallStaticMethod(GetAssemblyByNamePtr, "System.Private.CoreLib", &name, &fullname); corlibimage = New(assemblyHandle, name, fullname); CoreCLR::Free((void*)name); @@ -983,15 +1011,6 @@ MONO_API MonoClass* mono_get_string_class(void) return (MonoClass*)klass; } -/* - * jit.h -*/ - -MONO_API char* mono_get_runtime_build_info(void) -{ - return CoreCLR::CallStaticMethodInternal(TEXT("GetRuntimeInformation")); -} - /* * assembly.h */ @@ -1005,7 +1024,8 @@ MONO_API MONO_RT_EXTERNAL_ONLY MonoAssembly* mono_assembly_load_from_full(MonoIm MONO_API void mono_assembly_close(MonoAssembly* assembly) { - CoreCLR::CallStaticMethodInternal(TEXT("CloseAssembly"), ((CoreCLRAssembly*)assembly)->GetHandle()); + static void* CloseAssemblyPtr = CoreCLR::GetStaticMethodPointer(TEXT("CloseAssembly")); + CoreCLR::CallStaticMethod(CloseAssemblyPtr, ((CoreCLRAssembly*)assembly)->GetHandle()); Delete((CoreCLRAssembly*)assembly); } @@ -1053,7 +1073,8 @@ MONO_API void mono_debug_open_image_from_memory(MonoImage* image, const mono_byt MONO_API MONO_RT_EXTERNAL_ONLY MonoReflectionAssembly* mono_assembly_get_object(MonoDomain* domain, MonoAssembly* assembly) { - return (MonoReflectionAssembly*)CoreCLR::CallStaticMethodInternal(TEXT("GetAssemblyObject"), ((CoreCLRAssembly*)assembly)->GetFullname().Get()); + static void* GetAssemblyObjectPtr = CoreCLR::GetStaticMethodPointer(TEXT("GetAssemblyObject")); + return (MonoReflectionAssembly*)CoreCLR::CallStaticMethod(GetAssemblyObjectPtr, ((CoreCLRAssembly*)assembly)->GetFullname().Get()); } MONO_API MONO_RT_EXTERNAL_ONLY MonoReflectionType* mono_type_get_object(MonoDomain* domain, MonoType* type) @@ -1166,11 +1187,10 @@ MONO_API MONO_RT_EXTERNAL_ONLY MonoClass* mono_array_class_get(MonoClass* elemen MONO_API MONO_RT_EXTERNAL_ONLY MonoClassField* mono_class_get_field_from_name(MonoClass* klass, const char* name) { - StringAnsi name2(name); CoreCLRClass* mi = (CoreCLRClass*)klass; for (auto field : mi->GetFields()) { - if (field->GetName() == name2) + if (field->GetName() == name) return (MonoClassField*)field; } return nullptr; @@ -1178,11 +1198,10 @@ MONO_API MONO_RT_EXTERNAL_ONLY MonoClassField* mono_class_get_field_from_name(Mo MONO_API MonoProperty* mono_class_get_property_from_name(MonoClass* klass, const char* name) { - StringAnsi name2(name); CoreCLRClass* mi = (CoreCLRClass*)klass; for (auto prop : mi->GetProperties()) { - if (prop->GetName() == name2) + if (prop->GetName() == name) return (MonoProperty*)prop; } return nullptr; @@ -1195,7 +1214,8 @@ MONO_API int32_t mono_class_instance_size(MonoClass* klass) MONO_API int32_t mono_class_value_size(MonoClass* klass, uint32* align) { - return CoreCLR::CallStaticMethodInternal(TEXT("NativeSizeOf"), ((CoreCLRClass*)klass)->GetTypeHandle(), align); + static void* NativeSizeOfPtr = CoreCLR::GetStaticMethodPointer(TEXT("NativeSizeOf")); + return CoreCLR::CallStaticMethod(NativeSizeOfPtr, ((CoreCLRClass*)klass)->GetTypeHandle(), align); } MONO_API MonoClass* mono_class_from_mono_type(MonoType* type) @@ -1206,7 +1226,8 @@ MONO_API MonoClass* mono_class_from_mono_type(MonoType* type) MONO_API mono_bool mono_class_is_subclass_of(MonoClass* klass, MonoClass* klassc, mono_bool check_interfaces) { - return CoreCLR::CallStaticMethodInternal(TEXT("TypeIsSubclassOf"), ((CoreCLRClass*)klass)->GetTypeHandle(), ((CoreCLRClass*)klassc)->GetTypeHandle(), check_interfaces); + static void* TypeIsSubclassOfPtr = CoreCLR::GetStaticMethodPointer(TEXT("TypeIsSubclassOf")); + return CoreCLR::CallStaticMethod(TypeIsSubclassOfPtr, ((CoreCLRClass*)klass)->GetTypeHandle(), ((CoreCLRClass*)klassc)->GetTypeHandle(), check_interfaces); } MONO_API char* mono_type_get_name(MonoType* type) @@ -1227,17 +1248,20 @@ MONO_API MONO_RT_EXTERNAL_ONLY MonoClass* mono_class_get_element_class(MonoClass MONO_API MONO_RT_EXTERNAL_ONLY mono_bool mono_class_is_valuetype(MonoClass* klass) { - return (mono_bool)CoreCLR::CallStaticMethodInternal(TEXT("TypeIsValueType"), ((CoreCLRClass*)klass)->GetTypeHandle()); + static void* TypeIsValueTypePtr = CoreCLR::GetStaticMethodPointer(TEXT("TypeIsValueType")); + return (mono_bool)CoreCLR::CallStaticMethod(TypeIsValueTypePtr, ((CoreCLRClass*)klass)->GetTypeHandle()); } MONO_API MONO_RT_EXTERNAL_ONLY mono_bool mono_class_is_enum(MonoClass* klass) { - return (mono_bool)CoreCLR::CallStaticMethodInternal(TEXT("TypeIsEnum"), ((CoreCLRClass*)klass)->GetTypeHandle()); + static void* TypeIsEnumPtr = CoreCLR::GetStaticMethodPointer(TEXT("TypeIsEnum")); + return (mono_bool)CoreCLR::CallStaticMethod(TypeIsEnumPtr, ((CoreCLRClass*)klass)->GetTypeHandle()); } MONO_API MONO_RT_EXTERNAL_ONLY MonoClass* mono_class_get_parent(MonoClass* klass) { - void* parentHandle = CoreCLR::CallStaticMethodInternal(TEXT("GetClassParent"), ((CoreCLRClass*)klass)->GetTypeHandle()); + static void* GetClassParentPtr = CoreCLR::GetStaticMethodPointer(TEXT("GetClassParent")); + void* parentHandle = CoreCLR::CallStaticMethod(GetClassParentPtr, ((CoreCLRClass*)klass)->GetTypeHandle()); return (MonoClass*)classHandles[parentHandle]; } @@ -1426,7 +1450,8 @@ MONO_API mono_bool mono_type_is_byref(MonoType* type) MONO_API int mono_type_get_type(MonoType* type) { - return CoreCLR::CallStaticMethodInternal(TEXT("GetTypeMonoTypeEnum"), type); + static void* GetTypeMonoTypeEnumPtr = CoreCLR::GetStaticMethodPointer(TEXT("GetTypeMonoTypeEnum")); + return CoreCLR::CallStaticMethod(GetTypeMonoTypeEnumPtr, type); } MONO_API MonoClass* mono_type_get_class(MonoType* type) @@ -1482,7 +1507,8 @@ MONO_API uint32 mono_signature_get_param_count(MonoMethodSignature* sig) MONO_API mono_bool mono_signature_param_is_out(MonoMethodSignature* sig, int param_num) { CoreCLRMethod* mi = (CoreCLRMethod*)sig; - return CoreCLR::CallStaticMethodInternal(TEXT("GetMethodParameterIsOut"), mi->GetMethodHandle(), param_num); + static void* GetMethodParameterIsOutPtr = CoreCLR::GetStaticMethodPointer(TEXT("GetMethodParameterIsOut")); + return CoreCLR::CallStaticMethod(GetMethodParameterIsOutPtr, mi->GetMethodHandle(), param_num); } MONO_API int mono_type_stack_size(MonoType* type, int* alignment) @@ -1501,27 +1527,32 @@ MONO_API MonoException* mono_exception_from_name_msg(MonoImage* image, const cha MONO_API MonoException* mono_get_exception_null_reference(void) { - return (MonoException*)CoreCLR::CallStaticMethodInternal(TEXT("GetNullReferenceException")); + static void* GetNullReferenceExceptionPtr = CoreCLR::GetStaticMethodPointer(TEXT("GetNullReferenceException")); + return (MonoException*)CoreCLR::CallStaticMethod(GetNullReferenceExceptionPtr); } MONO_API MonoException* mono_get_exception_not_supported(const char* msg) { - return (MonoException*)CoreCLR::CallStaticMethodInternal(TEXT("GetNotSupportedException")); + static void* GetNotSupportedExceptionPtr = CoreCLR::GetStaticMethodPointer(TEXT("GetNotSupportedException")); + return (MonoException*)CoreCLR::CallStaticMethod(GetNotSupportedExceptionPtr); } MONO_API MONO_RT_EXTERNAL_ONLY MonoException* mono_get_exception_argument_null(const char* arg) { - return (MonoException*)CoreCLR::CallStaticMethodInternal(TEXT("GetArgumentNullException")); + static void* GetArgumentNullExceptionPtr = CoreCLR::GetStaticMethodPointer(TEXT("GetArgumentNullException")); + return (MonoException*)CoreCLR::CallStaticMethod(GetArgumentNullExceptionPtr); } MONO_API MonoException* mono_get_exception_argument(const char* arg, const char* msg) { - return (MonoException*)CoreCLR::CallStaticMethodInternal(TEXT("GetArgumentException")); + static void* GetArgumentExceptionPtr = CoreCLR::GetStaticMethodPointer(TEXT("GetArgumentException")); + return (MonoException*)CoreCLR::CallStaticMethod(GetArgumentExceptionPtr); } MONO_API MONO_RT_EXTERNAL_ONLY MonoException* mono_get_exception_argument_out_of_range(const char* arg) { - return (MonoException*)CoreCLR::CallStaticMethodInternal(TEXT("GetArgumentOutOfRangeException")); + static void* GetArgumentOutOfRangeExceptionPtr = CoreCLR::GetStaticMethodPointer(TEXT("GetArgumentOutOfRangeException")); + return (MonoException*)CoreCLR::CallStaticMethod(GetArgumentOutOfRangeExceptionPtr); } /* @@ -1532,7 +1563,8 @@ MONO_API MONO_RT_EXTERNAL_ONLY MonoImage* mono_image_open_from_data_with_name(ch { const char* name; const char* fullname; - void* assemblyHandle = CoreCLR::CallStaticMethodInternal(TEXT("LoadAssemblyImage"), data, data_len, path, &name, &fullname); + static void* LoadAssemblyImagePtr = CoreCLR::GetStaticMethodPointer(TEXT("LoadAssemblyImage")); + void* assemblyHandle = CoreCLR::CallStaticMethod(LoadAssemblyImagePtr, data, data_len, path, &name, &fullname); CoreCLRAssembly* assembly = New(assemblyHandle, name, fullname); CoreCLR::Free((void*)name); diff --git a/Source/Engine/Scripting/ManagedCLR/MCore.cpp b/Source/Engine/Scripting/ManagedCLR/MCore.cpp index cc0aca74f..f1b7763cb 100644 --- a/Source/Engine/Scripting/ManagedCLR/MCore.cpp +++ b/Source/Engine/Scripting/ManagedCLR/MCore.cpp @@ -136,21 +136,21 @@ bool MCore::LoadEngine() // Prepare managed side const String hostExecutable = Platform::GetExecutableFilePath(); - CoreCLR::CallStaticMethodInternal(TEXT("Init"), hostExecutable.Get()); + CoreCLR::CallStaticMethodByName(TEXT("Init"), hostExecutable.Get()); MRootDomain = New("Root"); MDomains.Add(MRootDomain); - char* buildInfo = mono_get_runtime_build_info(); + char* buildInfo = CoreCLR::CallStaticMethodByName(TEXT("GetRuntimeInformation")); LOG(Info, ".NET runtime version: {0}", String(buildInfo)); - mono_free(buildInfo); + CoreCLR::Free(buildInfo); return false; } void MCore::UnloadEngine() { - CoreCLR::CallStaticMethodInternal(TEXT("Exit")); + CoreCLR::CallStaticMethodByName(TEXT("Exit")); MDomains.ClearDelete(); MRootDomain = nullptr; } diff --git a/Source/Engine/Utilities/Utils.cs b/Source/Engine/Utilities/Utils.cs index 60eaad2d1..27cc24a10 100644 --- a/Source/Engine/Utilities/Utils.cs +++ b/Source/Engine/Utilities/Utils.cs @@ -341,9 +341,12 @@ namespace FlaxEngine return result; } +#if USE_NETCORE +#else [LibraryImport("FlaxEngine", EntryPoint = "FlaxEngine.Utils::Internal_ExtractArrayFromList")] [return: MarshalUsing(typeof(FlaxEngine.SystemArrayMarshaller))] internal static partial Array Internal_ExtractArrayFromList([MarshalUsing(typeof(FlaxEngine.GCHandleMarshaller))] object list); +#endif /// /// Reads the color from the binary stream. diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs index 77f217ea8..88113a756 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs @@ -1195,7 +1195,8 @@ namespace Flax.Build.Bindings #if USE_NETCORE if (!string.IsNullOrEmpty(marshallerName)) { - string marshallerDefinition = $$""" + contents.AppendLine(); + contents.AppendLine(String.Join("\n" + indent, (indent + $$""" [CustomMarshaller(typeof({{classInfo.Name}}), MarshalMode.ManagedToUnmanagedIn, typeof({{marshallerName}}.ManagedToNative))] [CustomMarshaller(typeof({{classInfo.Name}}), MarshalMode.UnmanagedToManagedOut, typeof({{marshallerName}}.ManagedToNative))] [CustomMarshaller(typeof({{classInfo.Name}}), MarshalMode.ElementIn, typeof({{marshallerName}}.ManagedToNative))] @@ -1207,34 +1208,33 @@ namespace Flax.Build.Bindings [CustomMarshaller(typeof({{classInfo.Name}}), MarshalMode.ElementRef, typeof({{marshallerName}}))] internal static class {{marshallerName}} { - public static class NativeToManaged - { - public static {{classInfo.Name}} ConvertToManaged(IntPtr unmanaged) => ({{classInfo.Name}})GCHandleMarshaller.NativeToManaged.ConvertToManaged(unmanaged); - public static void Free(IntPtr unmanaged) => GCHandleMarshaller.NativeToManaged.Free(unmanaged); - } - public static class ManagedToNative - { - public static IntPtr ConvertToUnmanaged({{classInfo.Name}} managed) => GCHandleMarshaller.ManagedToNative.ConvertToUnmanaged(managed); - public static void Free(IntPtr unmanaged) => GCHandleMarshaller.ManagedToNative.Free(unmanaged); - } - public struct Bidirectional - { - GCHandleMarshaller.Bidirectional marsh; - public void FromManaged({{classInfo.Name}} managed) => marsh.FromManaged(managed); - public IntPtr ToUnmanaged() => marsh.ToUnmanaged(); - public void FromUnmanaged(IntPtr unmanaged) => marsh.FromUnmanaged(unmanaged); - public {{classInfo.Name}} ToManaged() => ({{classInfo.Name}})marsh.ToManaged(); - public void Free() => marsh.Free(); - } - internal static {{classInfo.Name}} ConvertToManaged(IntPtr unmanaged) => ({{classInfo.Name}})GCHandleMarshaller.ConvertToManaged(unmanaged); - internal static IntPtr ConvertToUnmanaged({{classInfo.Name}} managed) => GCHandleMarshaller.ConvertToUnmanaged(managed); - internal static void Free(IntPtr unmanaged) => GCHandleMarshaller.Free(unmanaged); + public static class NativeToManaged + { + public static {{classInfo.Name}} ConvertToManaged(IntPtr unmanaged) => ({{classInfo.Name}})GCHandleMarshaller.NativeToManaged.ConvertToManaged(unmanaged); + public static void Free(IntPtr unmanaged) => GCHandleMarshaller.NativeToManaged.Free(unmanaged); + } + public static class ManagedToNative + { + public static IntPtr ConvertToUnmanaged({{classInfo.Name}} managed) => GCHandleMarshaller.ManagedToNative.ConvertToUnmanaged(managed); + public static void Free(IntPtr unmanaged) => GCHandleMarshaller.ManagedToNative.Free(unmanaged); + } + public struct Bidirectional + { + GCHandleMarshaller.Bidirectional marsh; + public void FromManaged({{classInfo.Name}} managed) => marsh.FromManaged(managed); + public IntPtr ToUnmanaged() => marsh.ToUnmanaged(); + public void FromUnmanaged(IntPtr unmanaged) => marsh.FromUnmanaged(unmanaged); + public {{classInfo.Name}} ToManaged() => ({{classInfo.Name}})marsh.ToManaged(); + public void Free() => marsh.Free(); + } + internal static {{classInfo.Name}} ConvertToManaged(IntPtr unmanaged) => ({{classInfo.Name}})GCHandleMarshaller.ConvertToManaged(unmanaged); + internal static IntPtr ConvertToUnmanaged({{classInfo.Name}} managed) => GCHandleMarshaller.ConvertToUnmanaged(managed); + internal static void Free(IntPtr unmanaged) => GCHandleMarshaller.Free(unmanaged); - internal static {{classInfo.Name}} ToManaged(IntPtr managed) => ({{classInfo.Name}})GCHandleMarshaller.ToManaged(managed); - internal static IntPtr ToNative({{classInfo.Name}} managed) => GCHandleMarshaller.ToNative(managed); + internal static {{classInfo.Name}} ToManaged(IntPtr managed) => ({{classInfo.Name}})GCHandleMarshaller.ToManaged(managed); + internal static IntPtr ToNative({{classInfo.Name}} managed) => GCHandleMarshaller.ToNative(managed); } - """; - contents.AppendLine(marshallerDefinition); + """).Split(new char[] { '\n'}))); } #endif // Namespace end From c8cc8991287c353b9c17063c76fe45c019b576b2 Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Tue, 20 Dec 2022 23:40:54 +0200 Subject: [PATCH 4/4] Optimize allocations in method parameter marshalling and invocation --- Source/Engine/Engine/NativeInterop.cs | 1716 ++++++++++------- Source/Engine/Engine/NativeInterop_Invoker.cs | 1055 ++++++++++ .../Bindings/BindingsGenerator.CSharp.cs | 27 +- 3 files changed, 2125 insertions(+), 673 deletions(-) create mode 100644 Source/Engine/Engine/NativeInterop_Invoker.cs diff --git a/Source/Engine/Engine/NativeInterop.cs b/Source/Engine/Engine/NativeInterop.cs index 841113c9b..4c06527eb 100644 --- a/Source/Engine/Engine/NativeInterop.cs +++ b/Source/Engine/Engine/NativeInterop.cs @@ -12,15 +12,11 @@ using System.Runtime.CompilerServices; using FlaxEngine.Assertions; using FlaxEngine.Utilities; using System.Runtime.InteropServices.Marshalling; -using System.Reflection.Metadata; -using System.Reflection.PortableExecutable; using FlaxEngine.Visject; using System.Diagnostics; using System.Collections; -using FlaxEditor.Content; - -#pragma warning disable CS1591 -#pragma warning disable CS8632 +using System.Buffers; +using System.Collections.Concurrent; [assembly: DisableRuntimeMarshalling] @@ -198,48 +194,88 @@ namespace FlaxEngine #endregion #region Wrappers + /// - /// Wrapper for managed arrays that needs to be pinned. + /// Wrapper for managed arrays which are passed to unmanaged code. /// - internal class ManagedArray + internal unsafe class ManagedArray { - internal Array array = null; - internal GCHandle handle; - internal int elementSize; + private Array array; + private GCHandle pinnedArrayHandle; + private IntPtr unmanagedData; + private int elementSize; + private int length; internal ManagedArray(Array arr) { - handle = GCHandle.Alloc(arr, GCHandleType.Pinned); - elementSize = Marshal.SizeOf(arr.GetType().GetElementType()); array = arr; + unmanagedData = IntPtr.Zero; + length = arr.Length; + elementSize = Marshal.SizeOf(arr.GetType().GetElementType()); + } + + private ManagedArray(void* ptr, int length, int elementSize) + { + array = null; + this.unmanagedData = new IntPtr(ptr); + this.length = length; + this.elementSize = elementSize; } ~ManagedArray() { - if (array != null) + if (unmanagedData != IntPtr.Zero || array != null) Release(); } internal void Release() { - handle.Free(); + GC.SuppressFinalize(this); + if (pinnedArrayHandle.IsAllocated) + pinnedArrayHandle.Free(); array = null; + if (unmanagedData != IntPtr.Zero) + { + Marshal.FreeHGlobal(unmanagedData); + unmanagedData = IntPtr.Zero; + } } - internal IntPtr PointerToPinnedArray => Marshal.UnsafeAddrOfPinnedArrayElement(array, 0); - - internal int Length => array.Length; - - internal static ManagedArray Get(ref T[] arr) + internal IntPtr PointerToPinnedArray { - return new ManagedArray(arr); + get + { + if (array != null) + { + // Pin the array when it's accessed for the first time + if (!pinnedArrayHandle.IsAllocated) + pinnedArrayHandle = GCHandle.Alloc(array); + return Marshal.UnsafeAddrOfPinnedArrayElement(array, 0); + } + else + return unmanagedData; + } } - internal static ManagedArray Get(Array arr) + internal void SetValue(IntPtr value, int index) { - ManagedArray managedArray = new ManagedArray(arr); - return managedArray; + if (array != null) + array.SetValue(value, index); + else + GetSpan()[index] = value; } + + internal int Length => length; + + internal int ElementSize => elementSize; + + internal Span GetSpan() where T : struct => array != null ? new Span((T[]) array) : new Span(unmanagedData.ToPointer(), length); + + internal T[] GetArray() where T : struct => array != null ? (T[])array : new Span(unmanagedData.ToPointer(), length).ToArray(); + + internal static ManagedArray Get(Array arr) => new ManagedArray(arr); + + internal static ManagedArray Get(T* ptr, int length) where T : unmanaged => new ManagedArray(ptr, length, Unsafe.SizeOf()); } internal static class ManagedString @@ -253,22 +289,10 @@ namespace FlaxEngine return IntPtr.Zero; else if (str == string.Empty) return GCHandle.ToIntPtr(EmptyStringHandle); -#if false - // HACK: Pin the string and pass the address to it including the length, no marshalling required - GCHandle handle = GCHandle.Alloc(str, GCHandleType.Pinned); - IntPtr ptr = handle.AddrOfPinnedObject() - sizeof(int); - pinnedStrings.TryAdd(ptr, handle); - return ptr; -#endif + Assert.IsTrue(str.Length > 0); -#if false - // HACK: Return address to the allocated string structure which includes the string length. - // We assume the string content is copied in native side before GC frees it. - IntPtr ptr = (Unsafe.Read(Unsafe.AsPointer(ref str)) + sizeof(int) * 2); -#endif - IntPtr ptr = GCHandle.ToIntPtr(GCHandle.Alloc(str, GCHandleType.Weak)); - return ptr; + return GCHandle.ToIntPtr(GCHandle.Alloc(str, GCHandleType.Weak)); } [System.Diagnostics.DebuggerStepThrough] @@ -277,7 +301,7 @@ namespace FlaxEngine if (ptr == IntPtr.Zero) return null; - return (string)GCHandle.FromIntPtr(ptr).Target; + return Unsafe.As(GCHandle.FromIntPtr(ptr).Target); } [System.Diagnostics.DebuggerStepThrough] @@ -294,7 +318,7 @@ namespace FlaxEngine } } - #endregion +#endregion #region Marshallers @@ -356,12 +380,12 @@ namespace FlaxEngine } } - [CustomMarshaller(typeof(System.Type), MarshalMode.Default, typeof(SystemTypeMarshaller))] + [CustomMarshaller(typeof(Type), MarshalMode.Default, typeof(SystemTypeMarshaller))] internal static class SystemTypeMarshaller { - internal static System.Type ConvertToManaged(IntPtr unmanaged) => (System.Type)GCHandleMarshaller.ConvertToManaged(unmanaged); + internal static Type ConvertToManaged(IntPtr unmanaged) => Unsafe.As(GCHandleMarshaller.ConvertToManaged(unmanaged)); - internal static IntPtr ConvertToUnmanaged(System.Type managed) + internal static IntPtr ConvertToUnmanaged(Type managed) { if (managed == null) return IntPtr.Zero; @@ -379,7 +403,7 @@ namespace FlaxEngine [CustomMarshaller(typeof(Exception), MarshalMode.Default, typeof(ExceptionMarshaller))] internal static class ExceptionMarshaller { - internal static Exception ConvertToManaged(IntPtr unmanaged) => (Exception)GCHandleMarshaller.ConvertToManaged(unmanaged); + internal static Exception ConvertToManaged(IntPtr unmanaged) => Unsafe.As(GCHandleMarshaller.ConvertToManaged(unmanaged)); internal static IntPtr ConvertToUnmanaged(Exception managed) => GCHandleMarshaller.ConvertToUnmanaged(managed); internal static void Free(IntPtr unmanaged) => GCHandleMarshaller.Free(unmanaged); } @@ -394,7 +418,7 @@ namespace FlaxEngine { public static class NativeToManaged { - public static FlaxEngine.Object ConvertToManaged(IntPtr unmanaged) => unmanaged != IntPtr.Zero ? (FlaxEngine.Object)GCHandle.FromIntPtr(unmanaged).Target : null; + public static FlaxEngine.Object ConvertToManaged(IntPtr unmanaged) => unmanaged != IntPtr.Zero ? Unsafe.As(GCHandle.FromIntPtr(unmanaged).Target) : null; } public static class ManagedToNative { @@ -405,17 +429,44 @@ namespace FlaxEngine [CustomMarshaller(typeof(CultureInfo), MarshalMode.Default, typeof(CultureInfoMarshaller))] internal static class CultureInfoMarshaller { - internal static CultureInfo ConvertToManaged(IntPtr unmanaged) => (CultureInfo)GCHandleMarshaller.ConvertToManaged(unmanaged); + internal static CultureInfo ConvertToManaged(IntPtr unmanaged) => Unsafe.As(GCHandleMarshaller.ConvertToManaged(unmanaged)); internal static IntPtr ConvertToUnmanaged(CultureInfo managed) => GCHandleMarshaller.ConvertToUnmanaged(managed); internal static void Free(IntPtr unmanaged) => GCHandleMarshaller.Free(unmanaged); } - [CustomMarshaller(typeof(Array), MarshalMode.Default, typeof(SystemArrayMarshaller))] + [CustomMarshaller(typeof(Array), MarshalMode.ManagedToUnmanagedIn, typeof(SystemArrayMarshaller.ManagedToNative))] + [CustomMarshaller(typeof(Array), MarshalMode.UnmanagedToManagedOut, typeof(SystemArrayMarshaller.ManagedToNative))] internal static class SystemArrayMarshaller { - internal static Array ConvertToManaged(IntPtr unmanaged) => (Array)GCHandleMarshaller.ConvertToManaged(unmanaged); - internal static IntPtr ConvertToUnmanaged(Array managed) => GCHandleMarshaller.ConvertToUnmanaged(managed); - internal static void Free(IntPtr unmanaged) => GCHandleMarshaller.Free(unmanaged); + public struct ManagedToNative + { + ManagedArray managedArray; + GCHandle handle; + + public void FromManaged(Array managed) + { + if (managed != null) + managedArray = ManagedArray.Get(managed); + } + + public IntPtr ToUnmanaged() + { + if (managedArray == null) + return IntPtr.Zero; + + handle = GCHandle.Alloc(managedArray); + return GCHandle.ToIntPtr(handle); + } + + public void Free() + { + if (managedArray != null) + { + handle.Free(); + managedArray.Release(); + } + } + } } [CustomMarshaller(typeof(Dictionary<,>), MarshalMode.ManagedToUnmanagedIn, typeof(DictionaryMarshaller<,>.ManagedToNative))] @@ -444,17 +495,31 @@ namespace FlaxEngine { Dictionary managed; IntPtr unmanaged; - public void FromManaged(Dictionary managed) { this.managed = managed; } - public IntPtr ToUnmanaged() { unmanaged = DictionaryMarshaller.ToNative(managed); return unmanaged; } - public void FromUnmanaged(IntPtr unmanaged) { this.unmanaged = unmanaged; } - public Dictionary ToManaged() { managed = DictionaryMarshaller.ToManaged(unmanaged); unmanaged = IntPtr.Zero; return managed; } + + public void FromManaged(Dictionary managed) => this.managed = managed; + + public IntPtr ToUnmanaged() + { + unmanaged = DictionaryMarshaller.ToNative(managed); + return unmanaged; + } + + public void FromUnmanaged(IntPtr unmanaged) => this.unmanaged = unmanaged; + + public Dictionary ToManaged() + { + managed = DictionaryMarshaller.ToManaged(unmanaged); + unmanaged = IntPtr.Zero; + return managed; + } + public void Free() => DictionaryMarshaller.Free(unmanaged); } - public static Dictionary ConvertToManaged(IntPtr unmanaged) => unmanaged != IntPtr.Zero ? (Dictionary)GCHandle.FromIntPtr(unmanaged).Target : null; + public static Dictionary ConvertToManaged(IntPtr unmanaged) => unmanaged != IntPtr.Zero ? Unsafe.As>(GCHandle.FromIntPtr(unmanaged).Target) : null; public static IntPtr ConvertToUnmanaged(Dictionary managed) => managed != null ? GCHandle.ToIntPtr(GCHandle.Alloc(managed)) : IntPtr.Zero; - public static Dictionary ToManaged(IntPtr unmanaged) => unmanaged != IntPtr.Zero ? (Dictionary)GCHandle.FromIntPtr(unmanaged).Target : null; + public static Dictionary ToManaged(IntPtr unmanaged) => unmanaged != IntPtr.Zero ? Unsafe.As>(GCHandle.FromIntPtr(unmanaged).Target) : null; public static IntPtr ToNative(Dictionary managed) => managed != null ? GCHandle.ToIntPtr(GCHandle.Alloc(managed)) : IntPtr.Zero; public static void Free(IntPtr unmanaged) { @@ -473,8 +538,7 @@ namespace FlaxEngine [CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.UnmanagedToManagedRef, typeof(ArrayMarshaller<,>.Bidirectional))] [CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.ElementRef, typeof(ArrayMarshaller<,>))] [ContiguousCollectionMarshaller] - internal static unsafe class ArrayMarshaller - where TUnmanagedElement : unmanaged + internal static unsafe class ArrayMarshaller where TUnmanagedElement : unmanaged { public static class NativeToManaged { @@ -483,20 +547,18 @@ namespace FlaxEngine if (unmanaged is null) return null; - ManagedArray array = (ManagedArray)GCHandle.FromIntPtr(new IntPtr(unmanaged)).Target; return new T[numElements]; } - internal static Span GetManagedValuesDestination(T[]? managed) - => managed; + internal static Span GetManagedValuesDestination(T[]? managed) => managed; - internal static ReadOnlySpan GetUnmanagedValuesSource(TUnmanagedElement* unmanagedValue, int numElements) + internal static ReadOnlySpan GetUnmanagedValuesSource(TUnmanagedElement* unmanaged, int numElements) { - if (unmanagedValue == null) + if (unmanaged == null) return ReadOnlySpan.Empty; - ManagedArray array = (ManagedArray)GCHandle.FromIntPtr(new IntPtr(unmanagedValue)).Target; - return new ReadOnlySpan(array.array as TUnmanagedElement[]); + ManagedArray managedArray = Unsafe.As(GCHandle.FromIntPtr(new IntPtr(unmanaged)).Target); + return managedArray.GetSpan(); } internal static void Free(TUnmanagedElement* unmanaged) @@ -505,7 +567,7 @@ namespace FlaxEngine return; GCHandle handle = GCHandle.FromIntPtr(new IntPtr(unmanaged)); - ((ManagedArray)handle.Target).Release(); + (Unsafe.As(handle.Target)).Release(); handle.Free(); } @@ -514,10 +576,11 @@ namespace FlaxEngine if (unmanaged == null) return Span.Empty; - ManagedArray array = (ManagedArray)GCHandle.FromIntPtr(new IntPtr(unmanaged)).Target; - return new Span(array.array as TUnmanagedElement[]); + ManagedArray managedArray = Unsafe.As(GCHandle.FromIntPtr(new IntPtr(unmanaged)).Target); + return managedArray.GetSpan(); } } + public static class ManagedToNative { internal static TUnmanagedElement* AllocateContainerForUnmanagedElements(T[]? managed, out int numElements) @@ -530,22 +593,21 @@ namespace FlaxEngine numElements = managed.Length; - ManagedArray array = ManagedArray.Get(new TUnmanagedElement[numElements]); - var ptr = GCHandle.ToIntPtr(GCHandle.Alloc(array)); + ManagedArray managedArray = ManagedArray.Get((TUnmanagedElement*)Marshal.AllocHGlobal(sizeof(TUnmanagedElement) * managed.Length), managed.Length); + var ptr = GCHandle.ToIntPtr(GCHandle.Alloc(managedArray)); return (TUnmanagedElement*)ptr; } - internal static ReadOnlySpan GetManagedValuesSource(T[]? managed) - => managed; + internal static ReadOnlySpan GetManagedValuesSource(T[]? managed) => managed; internal static Span GetUnmanagedValuesDestination(TUnmanagedElement* unmanaged, int numElements) { if (unmanaged == null) return Span.Empty; - ManagedArray array = (ManagedArray)GCHandle.FromIntPtr(new IntPtr(unmanaged)).Target; - return new Span(array.array as TUnmanagedElement[]); + ManagedArray managedArray = Unsafe.As(GCHandle.FromIntPtr(new IntPtr(unmanaged)).Target); + return managedArray.GetSpan(); } internal static void Free(TUnmanagedElement* unmanaged) @@ -554,10 +616,11 @@ namespace FlaxEngine return; GCHandle handle = GCHandle.FromIntPtr(new IntPtr(unmanaged)); - ((ManagedArray)handle.Target).Release(); + (Unsafe.As(handle.Target)).Release(); handle.Free(); } } + public struct Bidirectional { T[] managedArray; @@ -570,30 +633,27 @@ namespace FlaxEngine return; managedArray = managed; - unmanagedArray = ManagedArray.Get(new TUnmanagedElement[managed.Length]); + unmanagedArray = ManagedArray.Get((TUnmanagedElement*)Marshal.AllocHGlobal(sizeof(TUnmanagedElement) * managed.Length), managed.Length); handle = GCHandle.Alloc(unmanagedArray); } - public ReadOnlySpan GetManagedValuesSource() - => managedArray; + public ReadOnlySpan GetManagedValuesSource() => managedArray; public Span GetUnmanagedValuesDestination() { if (unmanagedArray == null) return Span.Empty; - return new Span(unmanagedArray.array as TUnmanagedElement[]); + return unmanagedArray.GetSpan(); } - public TUnmanagedElement* ToUnmanaged() - { - return (TUnmanagedElement*)GCHandle.ToIntPtr(handle); - } + public TUnmanagedElement* ToUnmanaged() => (TUnmanagedElement*)GCHandle.ToIntPtr(handle); - public void FromUnmanaged(TUnmanagedElement* value) + public void FromUnmanaged(TUnmanagedElement* unmanaged) { - ManagedArray arr = (ManagedArray)GCHandle.FromIntPtr(new IntPtr(value)).Target; - managedArray = new T[arr.Length]; + ManagedArray arr = Unsafe.As(GCHandle.FromIntPtr(new IntPtr(unmanaged)).Target); + if (managedArray == null || managedArray.Length != arr.Length) + managedArray = new T[arr.Length]; } public ReadOnlySpan GetUnmanagedValuesSource(int numElements) @@ -601,14 +661,12 @@ namespace FlaxEngine if (unmanagedArray == null) return ReadOnlySpan.Empty; - return new ReadOnlySpan(unmanagedArray.array as TUnmanagedElement[]); + return unmanagedArray.GetSpan(); } - public Span GetManagedValuesDestination(int numElements) - => managedArray; + public Span GetManagedValuesDestination(int numElements) => managedArray; - public T[] ToManaged() - => managedArray; + public T[] ToManaged() => managedArray; public void Free() { @@ -627,43 +685,34 @@ namespace FlaxEngine numElements = managed.Length; - ManagedArray array = ManagedArray.Get(new TUnmanagedElement[numElements]); - var ptr = GCHandle.ToIntPtr(GCHandle.Alloc(array)); + ManagedArray managedArray = ManagedArray.Get((TUnmanagedElement*)Marshal.AllocHGlobal(sizeof(TUnmanagedElement) * managed.Length), managed.Length); + IntPtr handle = GCHandle.ToIntPtr(GCHandle.Alloc(managedArray)); - return (TUnmanagedElement*)ptr; + return (TUnmanagedElement*)handle; } - internal static ReadOnlySpan GetManagedValuesSource(T[]? managed) - => managed; + internal static ReadOnlySpan GetManagedValuesSource(T[]? managed) => managed; internal static Span GetUnmanagedValuesDestination(TUnmanagedElement* unmanaged, int numElements) { if (unmanaged == null) return Span.Empty; - ManagedArray array = (ManagedArray)GCHandle.FromIntPtr(new IntPtr(unmanaged)).Target; - return new Span(array.array as TUnmanagedElement[]); + ManagedArray unmanagedArray = Unsafe.As(GCHandle.FromIntPtr(new IntPtr(unmanaged)).Target); + return unmanagedArray.GetSpan(); } - internal static T[]? AllocateContainerForManagedElements(TUnmanagedElement* unmanaged, int numElements) + internal static T[]? AllocateContainerForManagedElements(TUnmanagedElement* unmanaged, int numElements) => unmanaged is null ? null : new T[numElements]; + + internal static Span GetManagedValuesDestination(T[]? managed) => managed; + + internal static ReadOnlySpan GetUnmanagedValuesSource(TUnmanagedElement* unmanaged, int numElements) { - if (unmanaged is null) - return null; - - ManagedArray array = (ManagedArray)GCHandle.FromIntPtr(new IntPtr(unmanaged)).Target; - return new T[numElements]; - } - - internal static Span GetManagedValuesDestination(T[]? managed) - => managed; - - internal static ReadOnlySpan GetUnmanagedValuesSource(TUnmanagedElement* unmanagedValue, int numElements) - { - if (unmanagedValue == null) + if (unmanaged == null) return ReadOnlySpan.Empty; - ManagedArray array = (ManagedArray)GCHandle.FromIntPtr(new IntPtr(unmanagedValue)).Target; - return new ReadOnlySpan(array.array as TUnmanagedElement[]); + ManagedArray array = Unsafe.As(GCHandle.FromIntPtr(new IntPtr(unmanaged)).Target); + return array.GetSpan(); } internal static void Free(TUnmanagedElement* unmanaged) @@ -672,19 +721,9 @@ namespace FlaxEngine return; GCHandle handle = GCHandle.FromIntPtr(new IntPtr(unmanaged)); - ((ManagedArray)handle.Target).Release(); + Unsafe.As(handle.Target).Release(); handle.Free(); } - - internal static ref T GetPinnableReference(T[]? array) - { - if (array is null) - return ref Unsafe.NullRef(); - - ManagedArray managedArray = ManagedArray.Get(array); - var ptr = GCHandle.ToIntPtr(GCHandle.Alloc(managedArray)); - return ref Unsafe.AsRef(ptr.ToPointer()); - } } [CustomMarshaller(typeof(string), MarshalMode.ManagedToUnmanagedIn, typeof(StringMarshaller.ManagedToNative))] @@ -703,41 +742,45 @@ namespace FlaxEngine public static string ConvertToManaged(IntPtr unmanaged) => ManagedString.ToManaged(unmanaged); public static void Free(IntPtr unmanaged) => ManagedString.Free(unmanaged); } + public static class ManagedToNative { public static unsafe IntPtr ConvertToUnmanaged(string managed) { if (managed == null) return IntPtr.Zero; -#if false - // HACK: Pin the string and pass the address to it including the length, no marshalling required - GCHandle handle = GCHandle.Alloc(managed, GCHandleType.Pinned); - IntPtr ptr = handle.AddrOfPinnedObject() - sizeof(int); - pinnedStrings.TryAdd(ptr, handle); - return ptr; -#endif - -#if false - // HACK: Return address to the allocated string structure which includes the string length. - // We assume the string content is copied in native side before GC frees it. - IntPtr ptr = (Unsafe.Read(Unsafe.AsPointer(ref managed)) + sizeof(int) * 2); -#endif - IntPtr ptr = GCHandle.ToIntPtr(GCHandle.Alloc(managed, GCHandleType.Pinned)); - return ptr; + return GCHandle.ToIntPtr(GCHandle.Alloc(managed, GCHandleType.Weak)); } + public static void Free(IntPtr unmanaged) => ManagedString.Free(unmanaged); } + public struct Bidirectional { string managed; IntPtr unmanaged; - public void FromManaged(string managed) { this.managed = managed; } - public IntPtr ToUnmanaged() { unmanaged = ManagedString.ToNative(managed); return unmanaged; } - public void FromUnmanaged(IntPtr unmanaged) { this.unmanaged = unmanaged; } - public string ToManaged() { managed = ManagedString.ToManaged(unmanaged); unmanaged = IntPtr.Zero; return managed; } + + public void FromManaged(string managed) => this.managed = managed; + + public IntPtr ToUnmanaged() + { + unmanaged = ManagedString.ToNative(managed); + return unmanaged; + } + + public void FromUnmanaged(IntPtr unmanaged) => this.unmanaged = unmanaged; + + public string ToManaged() + { + managed = ManagedString.ToManaged(unmanaged); + unmanaged = IntPtr.Zero; + return managed; + } + public void Free() => ManagedString.Free(unmanaged); } + internal static string ConvertToManaged(IntPtr unmanaged) => ManagedString.ToManaged(unmanaged); internal static IntPtr ConvertToUnmanaged(string managed) => ManagedString.ToNative(managed); internal static void Free(IntPtr unmanaged) => ManagedString.Free(unmanaged); @@ -753,23 +796,26 @@ namespace FlaxEngine /// internal unsafe static partial class NativeInterop { + /// + /// Enables first-chance exception handling in invoked methods while debugger is attached. + /// + public static bool CatchExceptions = true; + internal static Dictionary AssemblyLocations = new Dictionary(); private static bool firstAssemblyLoaded = false; - - private static Dictionary typeCache = new Dictionary(); - private static Dictionary marshallableStructCache = new Dictionary(); + private static Dictionary typeCache = new Dictionary(); private static IntPtr boolTruePtr = GCHandle.ToIntPtr(GCHandle.Alloc((int)1, GCHandleType.Pinned)); private static IntPtr boolFalsePtr = GCHandle.ToIntPtr(GCHandle.Alloc((int)0, GCHandleType.Pinned)); private static List methodHandles = new(); private static List methodHandlesCollectible = new(); - private static Dictionary cachedDelegates = new Dictionary(); - private static Dictionary cachedDelegatesCollectible = new Dictionary(); - private static Dictionary typeHandleCache = new Dictionary(); - private static Dictionary typeHandleCacheCollectible = new Dictionary(); + private static ConcurrentDictionary cachedDelegates = new ConcurrentDictionary(); + private static ConcurrentDictionary cachedDelegatesCollectible = new ConcurrentDictionary(); + private static Dictionary typeHandleCache = new Dictionary(); + private static Dictionary typeHandleCacheCollectible = new Dictionary(); private static List fieldHandleCache = new(); private static List fieldHandleCacheCollectible = new(); private static Dictionary classAttributesCacheCollectible = new(); @@ -814,15 +860,12 @@ namespace FlaxEngine { } - internal static T[] GCHandleArrayToManagedArray(ManagedArray array) + internal static T[] GCHandleArrayToManagedArray(ManagedArray ptrArray) { - IntPtr[] ptrArray = (IntPtr[])array.array; - T[] managedArray = new T[array.Length]; + Span span = ptrArray.GetSpan(); + T[] managedArray = new T[ptrArray.Length]; for (int i = 0; i < managedArray.Length; i++) - { - IntPtr ptr = ptrArray[i]; - managedArray[i] = ptr != IntPtr.Zero ? (T)GCHandle.FromIntPtr(ptr).Target : default(T); - } + managedArray[i] = span[i] != IntPtr.Zero ? (T)GCHandle.FromIntPtr(span[i]).Target : default; return managedArray; } @@ -832,20 +875,17 @@ namespace FlaxEngine for (int i = 0; i < pointerArray.Length; i++) { var obj = array.GetValue(i); - pointerArray.SetValue(obj != null ? GCHandle.ToIntPtr(GCHandle.Alloc(obj)) : IntPtr.Zero, i); + if (obj != null) + pointerArray.SetValue(GCHandle.ToIntPtr(GCHandle.Alloc(obj)), i); } return pointerArray; } - internal static T[] NativeArrayToManagedArray(U[] nativeArray, Func toManagedFunc) + internal static T[] NativeArrayToManagedArray(Span nativeSpan, Func toManagedFunc) { - T[] managedArray = new T[nativeArray.Length]; - if (nativeArray.Length > 0) - { - Assert.IsTrue(toManagedFunc != null); - for (int i = 0; i < nativeArray.Length; i++) - managedArray[i] = toManagedFunc(nativeArray[i]); - } + T[] managedArray = new T[nativeSpan.Length]; + for (int i = 0; i < nativeSpan.Length; i++) + managedArray[i] = toManagedFunc(nativeSpan[i]); return managedArray; } @@ -929,7 +969,7 @@ namespace FlaxEngine string @namespace = string.Join('.', splits[0].Split('.').SkipLast(1)); string className = @namespace.Length > 0 ? splits[0].Substring(@namespace.Length + 1) : splits[0]; string parentClassName = ""; - if (className.Contains("+")) + if (className.Contains('+')) { parentClassName = className.Substring(0, className.LastIndexOf('+') + 1); className = className.Substring(parentClassName.Length); @@ -939,244 +979,564 @@ namespace FlaxEngine return FindType(internalAssemblyQualifiedName); } - private static IntPtr EnsureAlignment(IntPtr ptr, int alignment) + internal class ReferenceTypePlaceholder { } + internal struct ValueTypePlaceholder { } + + internal delegate object MarshalToManagedDelegate(IntPtr nativePtr, bool byRef); + internal delegate void MarshalToNativeDelegate(object managedObject, IntPtr nativePtr); + internal delegate void MarshalToNativeFieldDelegate(FieldInfo field, object fieldOwner, IntPtr nativePtr, out int fieldOffset); + + internal static ConcurrentDictionary toManagedMarshallers = new ConcurrentDictionary(1, 3); + internal static ConcurrentDictionary toNativeMarshallers = new ConcurrentDictionary(1, 3); + internal static ConcurrentDictionary toNativeFieldMarshallers = new ConcurrentDictionary(1, 3); + + internal static object MarshalToManaged(IntPtr nativePtr, Type type) { - if (ptr % alignment != 0) - ptr = IntPtr.Add(ptr, alignment - (int)(ptr % alignment)); - return ptr; + static MarshalToManagedDelegate Factory(Type type) + { + Type marshalType = type; + if (marshalType.IsByRef) + marshalType = marshalType.GetElementType(); + else if (marshalType.IsPointer) + marshalType = typeof(IntPtr); + + MethodInfo method = typeof(MarshalHelper<>).MakeGenericType(marshalType).GetMethod(nameof(MarshalHelper.ToManagedWrapper), BindingFlags.Static | BindingFlags.NonPublic); + return method.CreateDelegate(); + } + + if (toManagedMarshallers.TryGetValue(type, out var deleg)) + return deleg(nativePtr, type.IsByRef); + return toManagedMarshallers.GetOrAdd(type, Factory)(nativePtr, type.IsByRef); } - internal static void MarshalFromObject(object obj, Type objectType, ref IntPtr targetPtr) + internal static void MarshalToNative(object managedObject, IntPtr nativePtr, Type type) { - int readOffset = 0; - if (objectType == typeof(IntPtr)) + static MarshalToNativeDelegate Factory(Type type) { - targetPtr = EnsureAlignment(targetPtr, IntPtr.Size); - Marshal.WriteIntPtr(targetPtr, (IntPtr)obj); - readOffset = IntPtr.Size; - } - else if (objectType == typeof(byte)) - { - targetPtr = EnsureAlignment(targetPtr, sizeof(byte)); - Marshal.WriteByte(targetPtr, (byte)obj); - readOffset = sizeof(byte); - } - else if (objectType == typeof(bool)) - { - targetPtr = EnsureAlignment(targetPtr, sizeof(byte)); - Marshal.WriteByte(targetPtr, ((bool)obj) ? (byte)1 : (byte)0); - readOffset = sizeof(byte); - } - else if (objectType.IsEnum) - { - MarshalFromObject(obj, objectType.GetEnumUnderlyingType(), ref targetPtr); - } - else if (objectType == typeof(int) || objectType.Name == "Int32&") - { - targetPtr = EnsureAlignment(targetPtr, sizeof(int)); - Marshal.WriteInt32(targetPtr, (int)obj); - readOffset = sizeof(int); - } - else if (objectType == typeof(long)) - { - targetPtr = EnsureAlignment(targetPtr, sizeof(long)); - Marshal.WriteInt64(targetPtr, (long)obj); - readOffset = sizeof(long); - } - else if (objectType == typeof(short)) - { - targetPtr = EnsureAlignment(targetPtr, sizeof(short)); - Marshal.WriteInt16(targetPtr, (short)obj); - readOffset = sizeof(short); - } - else if (objectType == typeof(string)) - { - targetPtr = EnsureAlignment(targetPtr, IntPtr.Size); - IntPtr strPtr = ManagedString.ToNative(obj as string); - Marshal.WriteIntPtr(targetPtr, strPtr); - readOffset = IntPtr.Size; - } - else if (objectType.IsByRef) - throw new NotImplementedException(); - else if (objectType.IsClass) - { - if (objectType.IsPointer) - { - targetPtr = EnsureAlignment(targetPtr, IntPtr.Size); - var unboxed = Pointer.Unbox(obj); - if (unboxed == null) - Marshal.WriteIntPtr(targetPtr, IntPtr.Zero); - else if (obj is FlaxEngine.Object fobj) - Marshal.WriteIntPtr(targetPtr, fobj.__unmanagedPtr); - else - Marshal.WriteIntPtr(targetPtr, GCHandle.ToIntPtr(GCHandle.Alloc(obj, GCHandleType.Weak))); - readOffset = IntPtr.Size; - } + MethodInfo method; + if (type.IsValueType) + method = typeof(MarshalHelperValueType<>).MakeGenericType(type).GetMethod(nameof(MarshalHelperValueType.ToNativeWrapper), BindingFlags.Static | BindingFlags.NonPublic); else - { - targetPtr = EnsureAlignment(targetPtr, IntPtr.Size); - Marshal.WriteIntPtr(targetPtr, obj != null ? GCHandle.ToIntPtr(GCHandle.Alloc(obj, GCHandleType.Weak)) : IntPtr.Zero); - readOffset = IntPtr.Size; - } + method = typeof(MarshalHelperReferenceType<>).MakeGenericType(type).GetMethod(nameof(MarshalHelperReferenceType.ToNativeWrapper), BindingFlags.Static | BindingFlags.NonPublic); + return method.CreateDelegate(); } - else if (objectType.IsValueType) - { - if (GetMarshallableStructFields(objectType, out FieldInfo[] fields)) - { - IntPtr origPtr = targetPtr; - foreach (var field in fields) - MarshalFromObject(field.GetValue(obj), field.FieldType, ref targetPtr); - - Assert.IsTrue((targetPtr-origPtr) == Marshal.SizeOf(objectType)); - } - else - { - Marshal.StructureToPtr(obj, targetPtr, false); - readOffset = Marshal.SizeOf(objectType); - } - } + if (toNativeMarshallers.TryGetValue(type, out var deleg)) + deleg(managedObject, nativePtr); else - throw new NotImplementedException(objectType.FullName); - - targetPtr = IntPtr.Add(targetPtr, readOffset); + toNativeMarshallers.GetOrAdd(type, Factory)(managedObject, nativePtr); } - private static bool GetMarshallableStructFields(Type type, out FieldInfo[] fields) + internal static MarshalToNativeFieldDelegate GetToNativeFieldMarshallerDelegate(Type type) { - if (marshallableStructCache.TryGetValue(type, out fields)) - return fields != null; - - fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); - if (!fields.Any(x => x.FieldType.IsClass || x.FieldType.Name == "Boolean")) - fields = null; - - marshallableStructCache.Add(type, fields); - return fields != null; - } - - internal static object MarshalToObject(Type type, IntPtr ptr, out int readSize) - { - readSize = 0; - if (type.Name == "IntPtr&" || type.Name == "Byte*" || type.Name == "IntPtr") + static MarshalToNativeFieldDelegate Factory(Type type) { - readSize += IntPtr.Size; + MethodInfo method; + if (type.IsValueType) + method = typeof(MarshalHelperValueType<>).MakeGenericType(type).GetMethod(nameof(MarshalHelperValueType.ToNativeFieldWrapper), BindingFlags.Static | BindingFlags.NonPublic); + else + method = typeof(MarshalHelperReferenceType<>).MakeGenericType(type).GetMethod(nameof(MarshalHelperReferenceType.ToNativeFieldWrapper), BindingFlags.Static | BindingFlags.NonPublic); + return method.CreateDelegate(); + } + if (toNativeFieldMarshallers.TryGetValue(type, out var deleg)) + return deleg; + return toNativeFieldMarshallers.GetOrAdd(type, Factory); + } + + internal static void MarshalToNativeField(FieldInfo field, object fieldOwner, IntPtr nativePtr, out int fieldOffset) + { + GetToNativeFieldMarshallerDelegate(fieldOwner.GetType())(field, fieldOwner, nativePtr, out fieldOffset); + } + + /// + /// Helper class for managing stored marshaller delegates for each type. + /// + internal static class MarshalHelper + { + private delegate void MarshalToNativeTypedDelegate(ref T managedValue, IntPtr nativePtr); + private delegate void MarshalToManagedTypedDelegate(ref T managedValue, IntPtr nativePtr, bool byRef); + internal delegate void MarshalFieldTypedDelegate(FieldInfo field, ref T fieldOwner, IntPtr fieldPtr, out int fieldOffset); + + internal static FieldInfo[] marshallableFields; + internal static MarshalFieldTypedDelegate[] toManagedFieldMarshallers; + internal static MarshalFieldTypedDelegate[] toNativeFieldMarshallers; + + private static MarshalToNativeTypedDelegate toNativeTypedMarshaller; + private static MarshalToManagedTypedDelegate toManagedTypedMarshaller; + + static MarshalHelper() + { + Type type = typeof(T); + + // Setup marshallers for managed and native directions + MethodInfo toManagedMethod; + if (type.IsValueType) + toManagedMethod = typeof(MarshalHelperValueType<>).MakeGenericType(type).GetMethod(nameof(MarshalHelperValueType.ToManaged), BindingFlags.Static | BindingFlags.NonPublic); + else if (type.IsArray && type.GetElementType().IsValueType) + toManagedMethod = typeof(MarshalHelperValueType<>).MakeGenericType(type.GetElementType()).GetMethod(nameof(MarshalHelperValueType.ToManagedArray), BindingFlags.Static | BindingFlags.NonPublic); + else if (type.IsArray && !type.GetElementType().IsValueType) + toManagedMethod = typeof(MarshalHelperReferenceType<>).MakeGenericType(type.GetElementType()).GetMethod(nameof(MarshalHelperReferenceType.ToManagedArray), BindingFlags.Static | BindingFlags.NonPublic); + else + toManagedMethod = typeof(MarshalHelperReferenceType<>).MakeGenericType(type).GetMethod(nameof(MarshalHelperReferenceType.ToManaged), BindingFlags.Static | BindingFlags.NonPublic); + toManagedTypedMarshaller = toManagedMethod.CreateDelegate(); + + MethodInfo toNativeMethod; + if (type.IsValueType) + toNativeMethod = typeof(MarshalHelperValueType<>).MakeGenericType(type).GetMethod(nameof(MarshalHelperValueType.ToNative), BindingFlags.Static | BindingFlags.NonPublic); + else + toNativeMethod = typeof(MarshalHelperReferenceType<>).MakeGenericType(type).GetMethod(nameof(MarshalHelperReferenceType.ToNative), BindingFlags.Static | BindingFlags.NonPublic); + toNativeTypedMarshaller = toNativeMethod.CreateDelegate(); + + if (!type.IsPrimitive && !type.IsPointer && type != typeof(bool)) + { + // Setup field-by-field marshallers for reference types or structures containing references + marshallableFields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); + if (type.IsValueType && !marshallableFields.Any(x => (x.FieldType.IsClass && !x.FieldType.IsPointer) || x.FieldType.Name == "Boolean")) + marshallableFields = null; + else if (!type.IsValueType && !marshallableFields.Any()) + marshallableFields = null; + + if (marshallableFields != null) + { + toManagedFieldMarshallers = new MarshalFieldTypedDelegate[marshallableFields.Length]; + toNativeFieldMarshallers = new MarshalFieldTypedDelegate[marshallableFields.Length]; + for (int i = 0; i < marshallableFields.Length; i++) + { + FieldInfo field = marshallableFields[i]; + MethodInfo toManagedFieldMethod; + MethodInfo toNativeFieldMethod; + + if (field.FieldType.IsPointer) + { + toManagedFieldMethod = typeof(MarshalHelper<>).MakeGenericType(type).GetMethod(nameof(MarshalHelper.ToManagedFieldPointer), BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public); + toNativeFieldMethod = typeof(MarshalHelper<>).MakeGenericType(type).GetMethod(nameof(MarshalHelper.ToNativeFieldPointer), BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public); + } + else if (field.FieldType.IsValueType) + { + toManagedFieldMethod = typeof(MarshalHelper<>.ValueTypeField<>).MakeGenericType(type, field.FieldType).GetMethod(nameof(MarshalHelper.ValueTypeField.ToManagedField), BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public); + toNativeFieldMethod = typeof(MarshalHelper<>.ValueTypeField<>).MakeGenericType(type, field.FieldType).GetMethod(nameof(MarshalHelper.ValueTypeField.ToNativeField), BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public); + } + else if (field.FieldType.IsArray) + { + Type arrayElementType = field.FieldType.GetElementType(); + if (arrayElementType.IsValueType) + { + toManagedFieldMethod = typeof(MarshalHelper<>.ValueTypeField<>).MakeGenericType(type, arrayElementType).GetMethod(nameof(MarshalHelper.ValueTypeField.ToManagedFieldArray), BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public); + toNativeFieldMethod = typeof(MarshalHelper<>.ReferenceTypeField<>).MakeGenericType(type, field.FieldType).GetMethod(nameof(MarshalHelper.ReferenceTypeField.ToNativeField), BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public); + } + else + { + toManagedFieldMethod = typeof(MarshalHelper<>.ReferenceTypeField<>).MakeGenericType(type, field.FieldType).GetMethod(nameof(MarshalHelper.ReferenceTypeField.ToManagedField), BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public); + toNativeFieldMethod = typeof(MarshalHelper<>.ReferenceTypeField<>).MakeGenericType(type, field.FieldType).GetMethod(nameof(MarshalHelper.ReferenceTypeField.ToNativeField), BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public); + } + } + else + { + toManagedFieldMethod = typeof(MarshalHelper<>.ReferenceTypeField<>).MakeGenericType(type, field.FieldType).GetMethod(nameof(MarshalHelper.ReferenceTypeField.ToManagedField), BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public); + toNativeFieldMethod = typeof(MarshalHelper<>.ReferenceTypeField<>).MakeGenericType(type, field.FieldType).GetMethod(nameof(MarshalHelper.ReferenceTypeField.ToNativeField), BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public); + } + toManagedFieldMarshallers[i] = toManagedFieldMethod.CreateDelegate(); + toNativeFieldMarshallers[i] = toNativeFieldMethod.CreateDelegate(); + } + } + } + } + + internal static object ToManagedWrapper(IntPtr nativePtr, bool byRef) + { + T managed = default; + toManagedTypedMarshaller(ref managed, nativePtr, byRef); + return managed; + } + + internal static void ToManaged(ref T managedValue, IntPtr nativePtr, bool byRef) + { + toManagedTypedMarshaller(ref managedValue, nativePtr, byRef); + } + + internal static Array ToManagedArray(Span ptrSpan) + { + T[] arr = new T[ptrSpan.Length]; + for (int i = 0; i < arr.Length; i++) + toManagedTypedMarshaller(ref arr[i], ptrSpan[i], false); + return arr; + } + + internal static void ToNative(ref T managedValue, IntPtr nativePtr) + { + toNativeTypedMarshaller(ref managedValue, nativePtr); + } + + internal static void ToNativeField(FieldInfo field, ref T fieldOwner, IntPtr nativePtr, out int fieldOffset) + { + if (marshallableFields != null) + { + for (int i = 0; i < marshallableFields.Length; i++) + { + if (marshallableFields[i] == field) + { + toNativeFieldMarshallers[i](marshallableFields[i], ref fieldOwner, nativePtr, out fieldOffset); + return; + } + } + } + + throw new Exception($"Invalid field {field.Name} to marshal for type {typeof(T).Name}"); + } + + private static void ToManagedFieldPointer(FieldInfo field, ref T fieldOwner, IntPtr fieldPtr, out int fieldOffset) + { + ref IntPtr fieldValueRef = ref GetFieldReference(field, ref fieldOwner); + fieldValueRef = Unsafe.Read(fieldPtr.ToPointer()); + fieldOffset = IntPtr.Size; + } + + private static void ToNativeFieldPointer(FieldInfo field, ref T fieldOwner, IntPtr fieldPtr, out int fieldOffset) + { + ref IntPtr fieldValueRef = ref GetFieldReference(field, ref fieldOwner); + Unsafe.Write(fieldPtr.ToPointer(), fieldValueRef); + fieldOffset = IntPtr.Size; + } + + /// + /// Returns a reference to the value of the field. + /// + private static ref TField GetFieldReference(FieldInfo field, ref T fieldOwner) + { + Assert.IsTrue(!field.IsInitOnly); + + // Get the address of the field, source: https://stackoverflow.com/a/56512720 + byte* fieldPtr = (byte*)Unsafe.AsPointer(ref fieldOwner) + (Marshal.ReadInt32(field.FieldHandle.Value + 4 + IntPtr.Size) & 0xFFFFFF); + if (typeof(T).IsValueType) + return ref Unsafe.AsRef(fieldPtr); + else + return ref Unsafe.AsRef(fieldPtr + IntPtr.Size); + } + + private static IntPtr EnsureAlignment(IntPtr ptr, int alignment) + { + if (ptr % alignment != 0) + ptr = IntPtr.Add(ptr, alignment - (int)(ptr % alignment)); return ptr; } - else if (type.IsByRef) - { - return MarshalToObject(type.GetElementType(), ptr, out readSize); - } - else if (type == typeof(string)) - { - readSize += IntPtr.Size; - return ManagedString.ToManaged(ptr); - } - else if (type == typeof(bool)) - { - readSize += sizeof(byte); - return Marshal.ReadByte(ptr) != 0; - } - else if (type.IsEnum) - { - return Enum.ToObject(type, MarshalToObject(type.GetEnumUnderlyingType(), ptr, out readSize)); - } - else if (type.IsArray) - { - readSize += IntPtr.Size; - if (ptr == IntPtr.Zero) - return null; - Type elementType = type.GetElementType(); - ManagedArray arr = (ManagedArray)GCHandle.FromIntPtr(ptr).Target; - if (arr.array.GetType().GetElementType() == elementType) - return arr.array; - - IntPtr[] ptrs = (IntPtr[])arr.array; - Array marshalledArray = Array.CreateInstance(elementType, ptrs.Length); - for (int i = 0; i < marshalledArray.Length; i++) - marshalledArray.SetValue(MarshalToObject(elementType, ptrs[i], out _), i); - return marshalledArray; - } - else if (type.IsClass) + private static class ValueTypeField where TField : struct { - readSize += IntPtr.Size; - if (ptr == IntPtr.Zero) - return null; + private static int fieldAlignment; - var obj = GCHandle.FromIntPtr(ptr).Target; - return obj; - } - else if (type.IsValueType) - { - if (GetMarshallableStructFields(type, out FieldInfo[] fields)) + static ValueTypeField() { - object target = RuntimeHelpers.GetUninitializedObject(type); - IntPtr fieldPtr = ptr; + Type fieldType = typeof(TField); + if (fieldType.IsEnum) + fieldType = fieldType.GetEnumUnderlyingType(); + else if (fieldType == typeof(bool)) + fieldType = typeof(byte); - foreach (var field in fields) + if (fieldType.IsValueType && !fieldType.IsEnum && !fieldType.IsPrimitive) // Is it a structure? + { } + else if (fieldType.IsClass || fieldType.IsPointer) + fieldAlignment = IntPtr.Size; + else + fieldAlignment = Marshal.SizeOf(fieldType); + } + + internal static void ToManagedField(FieldInfo field, ref T fieldOwner, IntPtr fieldPtr, out int fieldOffset) + { + fieldOffset = Unsafe.SizeOf(); + if (fieldAlignment > 1) { - object fieldValue; - int size; - - if (field.FieldType.IsClass || field.FieldType == typeof(IntPtr)) - fieldValue = MarshalToObject(field.FieldType, Marshal.ReadIntPtr(fieldPtr), out size); - else - { - int fieldSize; - Type fieldType = field.FieldType; - if (field.FieldType.IsEnum) - fieldType = field.FieldType.GetEnumUnderlyingType(); - else if (field.FieldType == typeof(bool)) - fieldType = typeof(byte); - - if (fieldType.IsValueType && !fieldType.IsEnum && !fieldType.IsPrimitive) // Is it a structure? - fieldSize = 1; - else if (fieldType.IsClass || fieldType.IsPointer) - fieldSize = IntPtr.Size; - else - fieldSize = Marshal.SizeOf(fieldType); - fieldPtr = EnsureAlignment(fieldPtr, fieldSize); - - fieldValue = MarshalToObject(field.FieldType, fieldPtr, out size); - } - - field.SetValue(target, fieldValue); - - fieldPtr = IntPtr.Add(fieldPtr, size); + IntPtr fieldStartPtr = fieldPtr; + fieldPtr = EnsureAlignment(fieldPtr, fieldAlignment); + fieldOffset += (fieldPtr - fieldStartPtr).ToInt32(); } - var totalReadSize = fieldPtr.ToInt64() - ptr.ToInt64(); - Assert.IsTrue(totalReadSize == Marshal.SizeOf(type)); - readSize += (int)totalReadSize; - return target; + ref TField fieldValueRef = ref GetFieldReference(field, ref fieldOwner); + MarshalHelperValueType.ToManaged(ref fieldValueRef, fieldPtr, false); + } + + internal static void ToManagedFieldArray(FieldInfo field, ref T fieldOwner, IntPtr fieldPtr, out int fieldOffset) + { + fieldOffset = Unsafe.SizeOf(); + if (fieldAlignment > 1) + { + IntPtr fieldStartPtr = fieldPtr; + fieldPtr = EnsureAlignment(fieldPtr, fieldAlignment); + fieldOffset += (fieldPtr - fieldStartPtr).ToInt32(); + } + + ref TField[] fieldValueRef = ref GetFieldReference(field, ref fieldOwner); + MarshalHelperValueType.ToManagedArray(ref fieldValueRef, fieldPtr, false); + } + + internal static void ToNativeField(FieldInfo field, ref T fieldOwner, IntPtr fieldPtr, out int fieldOffset) + { + fieldOffset = Unsafe.SizeOf(); + if (fieldAlignment > 1) + { + IntPtr startPtr = fieldPtr; + fieldPtr = EnsureAlignment(fieldPtr, fieldAlignment); + fieldOffset += (fieldPtr - startPtr).ToInt32(); + } + + if (field.IsInitOnly) + { + //TypedReference ownerRef = __makeref(fieldOwner); + //TField fieldValue = (TField)field.GetValueDirect(ownerRef); + TField fieldValue = (TField)field.GetValue(fieldOwner); + MarshalHelperValueType.ToNative(ref fieldValue, fieldPtr); + } + else + { + ref TField fieldValueRef = ref GetFieldReference(field, ref fieldOwner); + MarshalHelperValueType.ToNative(ref fieldValueRef, fieldPtr); + } } } - else if (type.Name == "IDictionary") - { - readSize += IntPtr.Size; - if (ptr == IntPtr.Zero) - return null; - return GCHandle.FromIntPtr(ptr).Target; + private static class ReferenceTypeField where TField : class + { + internal static void ToManagedField(FieldInfo field, ref T fieldOwner, IntPtr fieldPtr, out int fieldOffset) + { + ref TField fieldValueRef = ref GetFieldReference(field, ref fieldOwner); + MarshalHelperReferenceType.ToManaged(ref fieldValueRef, Marshal.ReadIntPtr(fieldPtr), false); + fieldOffset = IntPtr.Size; + } + + internal static void ToNativeField(FieldInfo field, ref T fieldOwner, IntPtr fieldPtr, out int fieldOffset) + { + if (field.IsInitOnly) + { + //TypedReference ownerRef = __makeref(fieldOwner); + //TField fieldValue = (TField)field.GetValueDirect(ownerRef); + TField fieldValue = (TField)field.GetValue(fieldOwner); + MarshalHelperReferenceType.ToNative(ref fieldValue, fieldPtr); + } + else + { + ref TField fieldValueRef = ref GetFieldReference(field, ref fieldOwner); + MarshalHelperReferenceType.ToNative(ref fieldValueRef, fieldPtr); + } + fieldOffset = IntPtr.Size; + } + } + } + + internal static class MarshalHelperValueType where T : struct + { + internal static void ToNativeWrapper(object managedObject, IntPtr nativePtr) + { + ToNative(ref Unsafe.Unbox(managedObject), nativePtr); } - readSize += Marshal.SizeOf(type); - return Marshal.PtrToStructure(ptr, type); + internal static void ToNativeFieldWrapper(FieldInfo field, object fieldOwner, IntPtr nativePtr, out int fieldOffset) + { + MarshalHelper.ToNativeField(field, ref Unsafe.Unbox(fieldOwner), nativePtr, out fieldOffset); + } + + internal static void ToManaged(ref T managedValue, IntPtr nativePtr, bool byRef) + { + Type type = typeof(T); + if (type.IsByRef || byRef) + { + if (type.IsByRef) + type = type.GetElementType(); + Assert.IsTrue(type.IsValueType); + } + + if (type == typeof(IntPtr)) + managedValue = (T)(object)nativePtr; + else if (MarshalHelper.marshallableFields != null) + { + IntPtr fieldPtr = nativePtr; + for (int i = 0; i < MarshalHelper.marshallableFields.Length; i++) + { + MarshalHelper.toManagedFieldMarshallers[i](MarshalHelper.marshallableFields[i], ref managedValue, fieldPtr, out int fieldOffset); + fieldPtr += fieldOffset; + } + + Assert.IsTrue((fieldPtr - nativePtr) == Unsafe.SizeOf()); + } + else + managedValue = Unsafe.Read(nativePtr.ToPointer()); + } + + internal static void ToManagedArray(ref T[] managedValue, IntPtr nativePtr, bool byRef) + { + Assert.IsTrue(!byRef); + + Type elementType = typeof(T); + if (nativePtr != IntPtr.Zero) + { + ManagedArray managedArray = Unsafe.As(GCHandle.FromIntPtr(nativePtr).Target); + if (ArrayFactory.GetMarshalledType(elementType) == elementType) + managedValue = Unsafe.As(managedArray.GetArray()); + else + managedValue = Unsafe.As(MarshalHelper.ToManagedArray(managedArray.GetSpan())); + } + else + managedValue = null; + } + + internal static void ToNative(ref T managedValue, IntPtr nativePtr) + { + if (typeof(T).IsByRef) + throw new NotImplementedException(); + + if (MarshalHelper.marshallableFields != null) + { + IntPtr fieldPtr = nativePtr; + for (int i = 0; i < MarshalHelper.marshallableFields.Length; i++) + { + MarshalHelper.toNativeFieldMarshallers[i](MarshalHelper.marshallableFields[i], ref managedValue, nativePtr, out int fieldOffset); + nativePtr += fieldOffset; + } + + Assert.IsTrue((nativePtr - fieldPtr) == Unsafe.SizeOf()); + } + else + Unsafe.AsRef(nativePtr.ToPointer()) = managedValue; + } + } + + internal static class MarshalHelperReferenceType where T : class + { + internal static void ToNativeWrapper(object managedObject, IntPtr nativePtr) + { + T managedValue = Unsafe.As(managedObject); + ToNative(ref managedValue, nativePtr); + } + + internal static void ToNativeFieldWrapper(FieldInfo field, object managedObject, IntPtr nativePtr, out int fieldOffset) + { + T managedValue = Unsafe.As(managedObject); + MarshalHelper.ToNativeField(field, ref managedValue, nativePtr, out fieldOffset); + } + + internal static void ToManaged(ref T managedValue, IntPtr nativePtr, bool byRef) + { + Type type = typeof(T); + if (byRef) + nativePtr = Marshal.ReadIntPtr(nativePtr); + + if (type == typeof(string)) + managedValue = Unsafe.As(ManagedString.ToManaged(nativePtr)); + else if (type.IsClass) + managedValue = nativePtr != IntPtr.Zero ? Unsafe.As(GCHandle.FromIntPtr(nativePtr).Target) : null; + else if (type.IsInterface) // Dictionary + managedValue = nativePtr != IntPtr.Zero ? Unsafe.As(GCHandle.FromIntPtr(nativePtr).Target) : null; + else + throw new NotImplementedException(); + } + + internal static void ToManagedArray(ref T[] managedValue, IntPtr nativePtr, bool byRef) + { + Assert.IsTrue(!byRef); + + Type elementType = typeof(T); + if (nativePtr != IntPtr.Zero) + { + ManagedArray managedArray = Unsafe.As(GCHandle.FromIntPtr(nativePtr).Target); + managedValue = Unsafe.As(MarshalHelper.ToManagedArray(managedArray.GetSpan())); + } + else + managedValue = null; + } + + internal static void ToNative(ref T managedValue, IntPtr nativePtr) + { + Type type = typeof(T); + + IntPtr managedPtr; + if (type == typeof(string)) + managedPtr = ManagedString.ToNative(managedValue as string); + else if (type.IsPointer) + { + if (Pointer.Unbox(managedValue) == null) + managedPtr = IntPtr.Zero; + else if (managedValue is FlaxEngine.Object flaxObj) + managedPtr = flaxObj.__unmanagedPtr; + else + managedPtr = GCHandle.ToIntPtr(GCHandle.Alloc(managedValue, GCHandleType.Weak)); + } + else if (type == typeof(Type)) + managedPtr = managedValue != null ? GCHandle.ToIntPtr(GetOrAddTypeGCHandle((Type)(object)managedValue)) : IntPtr.Zero; + else + managedPtr = managedValue != null ? GCHandle.ToIntPtr(GCHandle.Alloc(managedValue, GCHandleType.Weak)) : IntPtr.Zero; + + Unsafe.Write(nativePtr.ToPointer(), managedPtr); + } + } + + internal class FieldBlob + { + internal FieldInfo field; + internal MarshalToNativeFieldDelegate toNativeMarshaller; + + internal FieldBlob(FieldInfo field, Type type) + { + this.field = field; + toNativeMarshaller = GetToNativeFieldMarshallerDelegate(type); + } + } + + internal class MethodBlob + { + internal Type[] parameterTypes; + internal MethodInfo method; + private Invoker.MarshalAndInvokeDelegate invokeDelegate; + private object delegInvoke; + + internal MethodBlob(MethodInfo method) + { + this.method = method; + parameterTypes = method.GetParameters().Select(x => x.ParameterType).ToArray(); + } + + internal bool TryGetDelegate(out Invoker.MarshalAndInvokeDelegate outDeleg, out object outDelegInvoke) + { + if (invokeDelegate == null) + { + List methodTypes = new List(); + if (!method.IsStatic) + methodTypes.Add(method.DeclaringType); + if (method.ReturnType != typeof(void)) + methodTypes.Add(method.ReturnType); + methodTypes.AddRange(parameterTypes); + + List genericParamTypes = new List(); + foreach (var type in methodTypes) + { + if (type.IsByRef) + genericParamTypes.Add(type.GetElementType()); + else if (type.IsPointer) + genericParamTypes.Add(typeof(IntPtr)); + else + genericParamTypes.Add(type); + } + + string typeName = $"FlaxEngine.NativeInterop+Invoker+Invoker{(method.IsStatic ? "Static" : "")}{(method.ReturnType != typeof(void) ? "Ret" : "NoRet")}{parameterTypes.Length}{(genericParamTypes.Count > 0 ? "`" + genericParamTypes.Count : "")}"; + Type invokerType = genericParamTypes.Count == 0 ? Type.GetType(typeName) : Type.GetType(typeName).MakeGenericType(genericParamTypes.ToArray()); + invokeDelegate = invokerType.GetMethod(nameof(Invoker.InvokerStaticNoRet0.MarshalAndInvoke), BindingFlags.Static | BindingFlags.NonPublic).CreateDelegate(); + delegInvoke = invokerType.GetMethod(nameof(Invoker.InvokerStaticNoRet0.CreateDelegate), BindingFlags.Static | BindingFlags.NonPublic).Invoke(null, new object[] { method }); + } + + outDeleg = invokeDelegate; + outDelegInvoke = delegInvoke; + + return outDeleg != null; + } } internal static GCHandle GetMethodGCHandle(MethodInfo method) { - Type[] parameterTypes = method.GetParameters().Select(x => x.ParameterType).ToArray(); + MethodBlob methodBlob = new MethodBlob(method); - GCHandle delegTupleHandle = GCHandle.Alloc(new Tuple(parameterTypes, method)); - - if (parameterTypes.Any(x => x.IsCollectible) || method.IsCollectible) - methodHandlesCollectible.Add(delegTupleHandle); + GCHandle handle = GCHandle.Alloc(methodBlob); + if (methodBlob.parameterTypes.Any(x => x.IsCollectible) || method.IsCollectible) + methodHandlesCollectible.Add(handle); else - methodHandles.Add(delegTupleHandle); - - return delegTupleHandle; + methodHandles.Add(handle); + return handle; } internal static GCHandle GetAssemblyHandle(Assembly assembly) @@ -1192,7 +1552,7 @@ namespace FlaxEngine [UnmanagedCallersOnly] internal static unsafe void GetManagedClasses(IntPtr assemblyHandle, ManagedClass** managedClasses, int* managedClassCount) { - Assembly assembly = (Assembly)GCHandle.FromIntPtr(assemblyHandle).Target; + Assembly assembly = Unsafe.As(GCHandle.FromIntPtr(assemblyHandle).Target); var assemblyTypes = GetAssemblyTypes(assembly); ManagedClass* arr = (ManagedClass*)Marshal.AllocCoTaskMem(Unsafe.SizeOf() * assemblyTypes.Length).ToPointer(); @@ -1215,7 +1575,7 @@ namespace FlaxEngine @namespace = Marshal.StringToCoTaskMemAnsi(type.Namespace ?? ""), typeAttributes = (uint)type.Attributes, }; - Marshal.StructureToPtr(managedClass, ptr, false); + Unsafe.Write(ptr.ToPointer(), managedClass); } *managedClasses = arr; @@ -1225,8 +1585,7 @@ namespace FlaxEngine [UnmanagedCallersOnly] internal static unsafe void GetManagedClassFromType(IntPtr typeHandle, ManagedClass* managedClass, IntPtr* assemblyHandle) { - var type = (Type)GCHandle.FromIntPtr(typeHandle).Target; - + Type type = Unsafe.As(GCHandle.FromIntPtr(typeHandle).Target); GCHandle classTypeHandle = GetOrAddTypeGCHandle(type); *managedClass = new ManagedClass() @@ -1243,15 +1602,15 @@ namespace FlaxEngine [UnmanagedCallersOnly] internal static void GetClassMethods(IntPtr typeHandle, ClassMethod** classMethods, int* classMethodsCount) { - Type type = (Type)GCHandle.FromIntPtr(typeHandle).Target; + Type type = Unsafe.As(GCHandle.FromIntPtr(typeHandle).Target); List methods = new List(); var staticMethods = type.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly); var instanceMethods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly); - foreach (MethodInfo methodInfo in staticMethods) - methods.Add(methodInfo); - foreach (MethodInfo methodInfo in instanceMethods) - methods.Add(methodInfo); + foreach (MethodInfo method in staticMethods) + methods.Add(method); + foreach (MethodInfo method in instanceMethods) + methods.Add(method); ClassMethod* arr = (ClassMethod*)Marshal.AllocCoTaskMem(Unsafe.SizeOf() * methods.Count).ToPointer(); for (int i = 0; i < methods.Count; i++) @@ -1264,7 +1623,7 @@ namespace FlaxEngine methodAttributes = (uint)methods[i].Attributes, }; classMethod.typeHandle = GCHandle.ToIntPtr(GetMethodGCHandle(methods[i])); - Marshal.StructureToPtr(classMethod, ptr, false); + Unsafe.Write(ptr.ToPointer(), classMethod); } *classMethods = arr; *classMethodsCount = methods.Count; @@ -1273,7 +1632,7 @@ namespace FlaxEngine [UnmanagedCallersOnly] internal static void GetClassFields(IntPtr typeHandle, ClassField** classFields, int* classFieldsCount) { - Type type = (Type)GCHandle.FromIntPtr(typeHandle).Target; + Type type = Unsafe.As(GCHandle.FromIntPtr(typeHandle).Target); var fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); ClassField* arr = (ClassField*)Marshal.AllocCoTaskMem(Unsafe.SizeOf() * fields.Length).ToPointer(); @@ -1281,7 +1640,9 @@ namespace FlaxEngine { IntPtr ptr = IntPtr.Add(new IntPtr(arr), Unsafe.SizeOf() * i); - GCHandle fieldHandle = GCHandle.Alloc(fields[i]); + FieldBlob fieldBlob = new FieldBlob(fields[i], type); + + GCHandle fieldHandle = GCHandle.Alloc(fieldBlob); if (type.IsCollectible) fieldHandleCacheCollectible.Add(fieldHandle); else @@ -1289,12 +1650,12 @@ namespace FlaxEngine ClassField classField = new ClassField() { - name = Marshal.StringToCoTaskMemAnsi(fields[i].Name), + name = Marshal.StringToCoTaskMemAnsi(fieldBlob.field.Name), fieldHandle = GCHandle.ToIntPtr(fieldHandle), - fieldTypeHandle = GCHandle.ToIntPtr(GetOrAddTypeGCHandle(fields[i].FieldType)), - fieldAttributes = (uint)fields[i].Attributes, + fieldTypeHandle = GCHandle.ToIntPtr(GetOrAddTypeGCHandle(fieldBlob.field.FieldType)), + fieldAttributes = (uint)fieldBlob.field.Attributes, }; - Marshal.StructureToPtr(classField, ptr, false); + Unsafe.Write(ptr.ToPointer(), classField); } *classFields = arr; *classFieldsCount = fields.Length; @@ -1303,7 +1664,7 @@ namespace FlaxEngine [UnmanagedCallersOnly] internal static void GetClassProperties(IntPtr typeHandle, ClassProperty** classProperties, int* classPropertiesCount) { - Type type = (Type)GCHandle.FromIntPtr(typeHandle).Target; + Type type = Unsafe.As(GCHandle.FromIntPtr(typeHandle).Target); var properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); ClassProperty* arr = (ClassProperty*)Marshal.AllocCoTaskMem(Unsafe.SizeOf() * properties.Length).ToPointer(); @@ -1328,7 +1689,7 @@ namespace FlaxEngine var setterHandle = GetMethodGCHandle(setterMethod); classProperty.setterHandle = GCHandle.ToIntPtr(setterHandle); } - Marshal.StructureToPtr(classProperty, ptr, false); + Unsafe.Write(ptr.ToPointer(), classProperty); } *classProperties = arr; *classPropertiesCount = properties.Length; @@ -1337,7 +1698,7 @@ namespace FlaxEngine [UnmanagedCallersOnly] internal static void GetClassAttributes(IntPtr typeHandle, ClassAttribute** classAttributes, int* classAttributesCount) { - Type type = (Type)GCHandle.FromIntPtr(typeHandle).Target; + Type type = Unsafe.As(GCHandle.FromIntPtr(typeHandle).Target); object[] attributeValues = type.GetCustomAttributes(false); Type[] attributeTypes = type.GetCustomAttributes(false).Select(x => x.GetType()).ToArray(); @@ -1359,7 +1720,7 @@ namespace FlaxEngine attributeTypeHandle = GCHandle.ToIntPtr(attributeTypeHandle), attributeHandle = GCHandle.ToIntPtr(attributeHandle), }; - Marshal.StructureToPtr(classAttribute, ptr, false); + Unsafe.Write(ptr.ToPointer(), classAttribute); } *classAttributes = arr; *classAttributesCount = attributeTypes.Length; @@ -1368,8 +1729,8 @@ namespace FlaxEngine [UnmanagedCallersOnly] internal static IntPtr GetCustomAttribute(IntPtr typeHandle, IntPtr attribHandle) { - Type type = (Type)GCHandle.FromIntPtr(typeHandle).Target; - Type attribType = (Type)GCHandle.FromIntPtr(attribHandle).Target; + Type type = Unsafe.As(GCHandle.FromIntPtr(typeHandle).Target); + Type attribType = Unsafe.As(GCHandle.FromIntPtr(attribHandle).Target); object attrib = type.GetCustomAttributes(false).FirstOrDefault(x => x.GetType() == attribType); if (attrib != null) @@ -1380,9 +1741,8 @@ namespace FlaxEngine [UnmanagedCallersOnly] internal static void GetClassInterfaces(IntPtr typeHandle, IntPtr* classInterfaces, int* classInterfacesCount) { - Type type = (Type)GCHandle.FromIntPtr(typeHandle).Target; - - var interfaces = type.GetInterfaces(); + Type type = Unsafe.As(GCHandle.FromIntPtr(typeHandle).Target); + Type[] interfaces = type.GetInterfaces(); // mono doesn't seem to return any interfaces, or returns only "immediate" interfaces? // FIXME? //foreach (Type interfaceType in type.BaseType.GetInterfaces()) @@ -1403,8 +1763,8 @@ namespace FlaxEngine [UnmanagedCallersOnly] internal static IntPtr GetMethodReturnType(IntPtr methodHandle) { - (Type[] parameterTypes, MethodInfo methodInfo) = (Tuple)GCHandle.FromIntPtr(methodHandle).Target; - Type returnType = methodInfo.ReturnType; + MethodBlob methodBlob = Unsafe.As(GCHandle.FromIntPtr(methodHandle).Target); + Type returnType = methodBlob.method.ReturnType; return GCHandle.ToIntPtr(GetTypeGCHandle(returnType)); } @@ -1412,17 +1772,14 @@ namespace FlaxEngine [UnmanagedCallersOnly] internal static void GetMethodParameterTypes(IntPtr methodHandle, IntPtr* typeHandles) { - (Type[] parameterTypes, MethodInfo methodInfo) = (Tuple)GCHandle.FromIntPtr(methodHandle).Target; - Type returnType = methodInfo.ReturnType; + MethodBlob methodBlob = Unsafe.As(GCHandle.FromIntPtr(methodHandle).Target); + Type returnType = methodBlob.method.ReturnType; - IntPtr arr = Marshal.AllocCoTaskMem(IntPtr.Size * parameterTypes.Length); - for (int i = 0; i < parameterTypes.Length; i++) + IntPtr arr = Marshal.AllocCoTaskMem(IntPtr.Size * methodBlob.parameterTypes.Length); + for (int i = 0; i < methodBlob.parameterTypes.Length; i++) { - IntPtr ptr = IntPtr.Add(new IntPtr(arr), IntPtr.Size * i); - - GCHandle typeHandle = GetOrAddTypeGCHandle(parameterTypes[i]); - - Marshal.WriteIntPtr(ptr, GCHandle.ToIntPtr(typeHandle)); + GCHandle typeHandle = GetOrAddTypeGCHandle(methodBlob.parameterTypes[i]); + Marshal.WriteIntPtr(IntPtr.Add(new IntPtr(arr), IntPtr.Size * i), GCHandle.ToIntPtr(typeHandle)); } *typeHandles = arr; } @@ -1433,73 +1790,88 @@ namespace FlaxEngine [UnmanagedCallersOnly] internal static IntPtr GetStringPointer(IntPtr stringHandle) { - string str = (string)GCHandle.FromIntPtr(stringHandle).Target; + string str = Unsafe.As(GCHandle.FromIntPtr(stringHandle).Target); IntPtr ptr = (Unsafe.Read(Unsafe.AsPointer(ref str)) + sizeof(int) * 2); + if (ptr < 0x1024) + throw new Exception("null string ptr"); return ptr; } [UnmanagedCallersOnly] internal static IntPtr NewObject(IntPtr typeHandle) { - Type classType = (Type)GCHandle.FromIntPtr(typeHandle).Target; - - if (classType == typeof(Script)) + Type type = Unsafe.As(GCHandle.FromIntPtr(typeHandle).Target); + if (type == typeof(Script)) { // FIXME: Script is an abstract type which can not be instantiated - classType = typeof(VisjectScript); + type = typeof(VisjectScript); } - object obj = RuntimeHelpers.GetUninitializedObject(classType); + object value = RuntimeHelpers.GetUninitializedObject(type); + return GCHandle.ToIntPtr(GCHandle.Alloc(value)); + } - IntPtr handle = GCHandle.ToIntPtr(GCHandle.Alloc(obj)); - return handle; + internal static class ArrayFactory + { + private static class Internal + { + internal static Array CreateArrayDelegate(long size) => new T[size]; + } + + private delegate Array CreateArrayDelegate(long size); + + private static ConcurrentDictionary createArrayDelegates = new ConcurrentDictionary(1, 3); + + private static CreateArrayDelegate GetCreateArrayDelegate(Type type) + { + static CreateArrayDelegate Factory(Type type) + { + Type marshalType; + if (IsBlittable(type)) + marshalType = type; + else + marshalType = GetInternalType(type) ?? typeof(IntPtr); + + MethodInfo method = typeof(Internal<>).MakeGenericType(marshalType).GetMethod(nameof(Internal.CreateArrayDelegate), BindingFlags.Static | BindingFlags.NonPublic); + return method.CreateDelegate(); + } + + if (createArrayDelegates.TryGetValue(type, out var deleg)) + return deleg; + return createArrayDelegates.GetOrAdd(type, Factory); + } + + internal static Array CreateArray(Type type, long size) => GetCreateArrayDelegate(type)(size); + + internal static Type GetMarshalledType(Type elementType) => GetCreateArrayDelegate(elementType).Method.DeclaringType.GenericTypeArguments[0]; } [UnmanagedCallersOnly] internal static IntPtr NewArray(IntPtr typeHandle, long size) { - Type type = (Type)GCHandle.FromIntPtr(typeHandle).Target; - Type elementType; - if (IsBlittable(type)) - elementType = type; - else - elementType = GetInternalType(type) ?? typeof(IntPtr); - Array arr = Array.CreateInstance(elementType, size); - ManagedArray managedArray = new ManagedArray(arr); + Type type = Unsafe.As(GCHandle.FromIntPtr(typeHandle).Target); + + Array arr = ArrayFactory.CreateArray(type, size); + ManagedArray managedArray = ManagedArray.Get(arr); return GCHandle.ToIntPtr(GCHandle.Alloc(managedArray/*, GCHandleType.Weak*/)); } [UnmanagedCallersOnly] internal static unsafe IntPtr GetArrayPointerToElement(IntPtr arrayHandle, int size, int index) { - object obj = GCHandle.FromIntPtr(arrayHandle).Target; - if (obj is ManagedArray managedArray) - { - if (managedArray.Length == 0) - return IntPtr.Zero; - Assert.IsTrue(index >= 0 && index < managedArray.Length); - IntPtr ptr = IntPtr.Add(managedArray.PointerToPinnedArray, size * index); - return ptr; - } - else - { - Array array = (Array)obj; - if (array.Length == 0) - return IntPtr.Zero; - Assert.IsTrue(index >= 0 && index < array.Length); - IntPtr ptr = Marshal.UnsafeAddrOfPinnedArrayElement(array, index); - return ptr; - } + ManagedArray managedArray = Unsafe.As(GCHandle.FromIntPtr(arrayHandle).Target); + if (managedArray.Length == 0) + return IntPtr.Zero; + + Assert.IsTrue(index >= 0 && index < managedArray.Length); + return IntPtr.Add(managedArray.PointerToPinnedArray, size * index); } [UnmanagedCallersOnly] - internal static int GetArrayLength(IntPtr handlePtr) + internal static int GetArrayLength(IntPtr arrayHandle) { - object obj = GCHandle.FromIntPtr(handlePtr).Target; - if (obj is ManagedArray managedArray) - return managedArray.Length; - else - return ((Array)obj).Length; + ManagedArray managedArray = Unsafe.As(GCHandle.FromIntPtr(arrayHandle).Target); + return managedArray.Length; } [UnmanagedCallersOnly] @@ -1532,9 +1904,8 @@ namespace FlaxEngine [UnmanagedCallersOnly] internal static IntPtr BoxValue(IntPtr typeHandle, IntPtr valuePtr) { - Type type = (Type)GCHandle.FromIntPtr(typeHandle).Target; - - object value = MarshalToObject(type, valuePtr, out _); + Type type = Unsafe.As(GCHandle.FromIntPtr(typeHandle).Target); + object value = MarshalToManaged(valuePtr, type); return GCHandle.ToIntPtr(GCHandle.Alloc(value, GCHandleType.Weak)); } @@ -1546,111 +1917,142 @@ namespace FlaxEngine return GCHandle.ToIntPtr(GetOrAddTypeGCHandle(classType)); } - internal unsafe class StructUnboxer where T : struct + internal static class ValueTypeUnboxer { - public StructUnboxer(object obj, IntPtr ptr) + private static ConcurrentDictionary unboxers = new ConcurrentDictionary(1, 3); + private static MethodInfo unboxerMethod = typeof(ValueTypeUnboxer).GetMethod(nameof(ValueTypeUnboxer.UnboxPointer), BindingFlags.Static | BindingFlags.NonPublic); + private delegate IntPtr UnboxerDelegate(object value); + + private static UnboxerDelegate UnboxerDelegateFactory(Type type) { - IntPtr* pin = (IntPtr*)ptr; - *pin = new IntPtr(Unsafe.AsPointer(ref Unsafe.Unbox(obj))); + return unboxerMethod.MakeGenericMethod(type).CreateDelegate(); + } + + internal static IntPtr GetPointer(object value) + { + Type type = value.GetType(); + if (unboxers.TryGetValue(type, out var deleg)) + return deleg(value); + return unboxers.GetOrAdd(type, UnboxerDelegateFactory)(value); + } + + private static unsafe IntPtr UnboxPointer(object value) where T : struct + { + return new IntPtr(Unsafe.AsPointer(ref Unsafe.Unbox(value))); } } + /// + /// Returns the address of the boxed value type. + /// [UnmanagedCallersOnly] internal unsafe static IntPtr UnboxValue(IntPtr handlePtr) { GCHandle handle = GCHandle.FromIntPtr(handlePtr); - object obj = handle.Target; - Type type = obj.GetType(); + object value = handle.Target; + Type type = value.GetType(); if (!type.IsValueType) return handlePtr; // HACK: Get the address of a non-pinned value - IntPtr ptr = IntPtr.Zero; - IntPtr* addr = &ptr; - Activator.CreateInstance(typeof(StructUnboxer<>).MakeGenericType(type), obj, new IntPtr(addr)); - - return ptr; + return ValueTypeUnboxer.GetPointer(value); } [UnmanagedCallersOnly] internal static void RaiseException(IntPtr exceptionHandle) { - var exception = (Exception)GCHandle.FromIntPtr(exceptionHandle).Target; + Exception exception = Unsafe.As(GCHandle.FromIntPtr(exceptionHandle).Target); throw exception; } [UnmanagedCallersOnly] - internal static void ObjectInit(IntPtr handle) + internal static void ObjectInit(IntPtr objectHandle) { - object obj = GCHandle.FromIntPtr(handle).Target; - Type classType = obj.GetType(); + object obj = GCHandle.FromIntPtr(objectHandle).Target; + Type type = obj.GetType(); - var ctors = classType.GetMember(".ctor", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); - var ctor = ctors[0] as ConstructorInfo; + ConstructorInfo ctor = type.GetMember(".ctor", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).First() as ConstructorInfo; ctor.Invoke(obj, null); } [UnmanagedCallersOnly] - internal static IntPtr InvokeMethod(IntPtr instance, IntPtr methodHandle, IntPtr paramData, IntPtr exceptionPtr) + internal static IntPtr InvokeMethod(IntPtr instancePtr, IntPtr methodHandle, IntPtr paramPtr, IntPtr exceptionPtr) { - (Type[] parameterTypes, MethodInfo methodInfo) = (Tuple)GCHandle.FromIntPtr(methodHandle).Target; - int numParams = parameterTypes.Length; - - var intPtrParams = stackalloc IntPtr[numParams]; - var objParams = new object[numParams]; - - IntPtr curptr = paramData; - for (int i = 0; i < numParams; i++) - { - intPtrParams[i] = Marshal.ReadIntPtr(curptr); - curptr = IntPtr.Add(curptr, sizeof(IntPtr)); - objParams[i] = MarshalToObject(parameterTypes[i], intPtrParams[i], out _); - } + MethodBlob methodBlob = Unsafe.As(GCHandle.FromIntPtr(methodHandle).Target); object returnObject; - try + if (methodBlob.TryGetDelegate(out var methodDelegate, out var methodDelegateContext)) { - object inst = instance != IntPtr.Zero ? GCHandle.FromIntPtr(instance).Target : null; - returnObject = methodInfo.Invoke(inst, objParams); - } - catch (Exception exception) - { - // The internal exception thrown in MethodInfo.Invoke is caught here - Exception realException = exception; - if (exception.InnerException != null && exception.TargetSite.ReflectedType.Name == "MethodInvoker") - realException = exception.InnerException; - - if (exceptionPtr != IntPtr.Zero) - Marshal.WriteIntPtr(exceptionPtr, GCHandle.ToIntPtr(GCHandle.Alloc(realException, GCHandleType.Weak))); - else - throw realException; - return IntPtr.Zero; - } - - // Marshal reference parameters back to original unmanaged references - for (int i = 0; i < numParams; i++) - { - Type parameterType = parameterTypes[i]; - if (parameterType.IsByRef) + // Fast path, invoke the method with minimal allocations + try { - IntPtr paramPtr = intPtrParams[i]; - MarshalFromObject(objParams[i], parameterType.GetElementType(), ref paramPtr); + returnObject = methodDelegate(methodDelegateContext, instancePtr, paramPtr); + } + catch (Exception exception) + { + if (Debugger.IsAttached && CatchExceptions) + throw; + + Marshal.WriteIntPtr(exceptionPtr, GCHandle.ToIntPtr(GCHandle.Alloc(exception, GCHandleType.Weak))); + return IntPtr.Zero; + } + } + else + { + // Slow path, method parameters needs to be stored in heap + int numParams = methodBlob.parameterTypes.Length; + IntPtr* nativePtrs = stackalloc IntPtr[numParams]; + object[] methodParameters = new object[numParams]; + + for (int i = 0; i < numParams; i++) + { + nativePtrs[i] = Marshal.ReadIntPtr(IntPtr.Add(paramPtr, sizeof(IntPtr) * i)); + methodParameters[i] = MarshalToManaged(nativePtrs[i], methodBlob.parameterTypes[i]); + } + + try + { + returnObject = methodBlob.method.Invoke(instancePtr != IntPtr.Zero ? GCHandle.FromIntPtr(instancePtr).Target : null, methodParameters); + } + catch (Exception exception) + { + if (Debugger.IsAttached && CatchExceptions) + throw; + + // The internal exception thrown in MethodInfo.Invoke is caught here + Exception realException = exception; + if (exception.InnerException != null && exception.TargetSite.ReflectedType.Name == "MethodInvoker") + realException = exception.InnerException; + + if (exceptionPtr != IntPtr.Zero) + Marshal.WriteIntPtr(exceptionPtr, GCHandle.ToIntPtr(GCHandle.Alloc(realException, GCHandleType.Weak))); + else + throw realException; + return IntPtr.Zero; + } + + // Marshal reference parameters back to original unmanaged references + for (int i = 0; i < numParams; i++) + { + Type parameterType = methodBlob.parameterTypes[i]; + if (parameterType.IsByRef) + MarshalToNative(methodParameters[i], nativePtrs[i], parameterType.GetElementType()); } } if (returnObject is not null) { - if (returnObject is string returnStr) - return ManagedString.ToNative(returnStr); - else if (returnObject is IntPtr returnPtr) - return returnPtr; - else if (returnObject is bool boolValue) - return boolValue ? boolTruePtr : boolFalsePtr; - else if (returnObject is Type typeValue) - return GCHandle.ToIntPtr(GetOrAddTypeGCHandle(typeValue)); - else if (returnObject is object[] objectArray) - return GCHandle.ToIntPtr(GCHandle.Alloc(ManagedArray.Get(ManagedArrayToGCHandleArray(objectArray)), GCHandleType.Weak)); + if (methodBlob.method.ReturnType == typeof(string)) + return ManagedString.ToNative(Unsafe.As(returnObject)); + else if (methodBlob.method.ReturnType == typeof(IntPtr)) + return (IntPtr)returnObject; + else if (methodBlob.method.ReturnType == typeof(bool)) + return (bool)returnObject ? boolTruePtr : boolFalsePtr; + else if (methodBlob.method.ReturnType == typeof(Type)) + return GCHandle.ToIntPtr(GetOrAddTypeGCHandle(Unsafe.As(returnObject))); + else if (methodBlob.method.ReturnType == typeof(object[])) + return GCHandle.ToIntPtr(GCHandle.Alloc(ManagedArray.Get(ManagedArrayToGCHandleArray(Unsafe.As(returnObject))), GCHandleType.Weak)); else return GCHandle.ToIntPtr(GCHandle.Alloc(returnObject, GCHandleType.Weak)); } @@ -1660,70 +2062,47 @@ namespace FlaxEngine [UnmanagedCallersOnly] internal static IntPtr GetMethodUnmanagedFunctionPointer(IntPtr methodHandle) { - (Type[] parameterTypes, MethodInfo methodInfo) = (Tuple)GCHandle.FromIntPtr(methodHandle).Target; - int numParams = parameterTypes.Length; - parameterTypes = parameterTypes.Append(methodInfo.ReturnType).ToArray(); + MethodBlob methodBlob = Unsafe.As(GCHandle.FromIntPtr(methodHandle).Target); // Wrap the method call, this is needed to get the object instance from GCHandle and to pass the exception back to native side - var invokeThunk = typeof(ThunkContext).GetMethod("InvokeThunk"); - parameterTypes = invokeThunk.GetParameters().Select(x => x.ParameterType).Append(invokeThunk.ReturnType).ToArray(); - Type delegateType = DelegateHelpers.NewDelegateType(parameterTypes); - - var context = new ThunkContext(); - context.methodInfo = methodInfo; - context.numParams = numParams; - context.objParams = new object[numParams]; - context.parameterTypes = methodInfo.GetParameters().Select(x => x.ParameterType).ToArray(); - - Delegate methodDelegate = Delegate.CreateDelegate(delegateType, context, invokeThunk); + MethodInfo invokeThunk = typeof(ThunkContext).GetMethod(nameof(ThunkContext.InvokeThunk)); + Type delegateType = DelegateHelpers.MakeNewCustomDelegate(invokeThunk.GetParameters().Select(x => x.ParameterType).Append(invokeThunk.ReturnType).ToArray()); + ThunkContext context = new ThunkContext(methodBlob.method); + Delegate methodDelegate = invokeThunk.CreateDelegate(delegateType, context); IntPtr functionPtr = Marshal.GetFunctionPointerForDelegate(methodDelegate); // Keep a reference to the delegate to prevent it from being garbage collected - var delegates = cachedDelegates; - if (parameterTypes.Any(x => scriptingAssemblyLoadContext.Assemblies.Contains(x.Module.Assembly))) - delegates = cachedDelegatesCollectible; - lock (delegates) - { - delegates[functionPtr] = methodDelegate; - } + if (methodBlob.method.IsCollectible) + cachedDelegatesCollectible[functionPtr] = methodDelegate; + else + cachedDelegates[functionPtr] = methodDelegate; return functionPtr; } [UnmanagedCallersOnly] - internal static void FieldSetValue(IntPtr handle, IntPtr fieldHandle, IntPtr value) + internal static void FieldSetValue(IntPtr fieldOwnerHandle, IntPtr fieldHandle, IntPtr valuePtr) { - var obj = GCHandle.FromIntPtr(handle).Target; - FieldInfo fieldInfo = (FieldInfo)GCHandle.FromIntPtr(fieldHandle).Target; - - var val = Marshal.PtrToStructure(value, fieldInfo.FieldType); - fieldInfo.SetValue(obj, val); + object fieldOwner = GCHandle.FromIntPtr(fieldOwnerHandle).Target; + FieldBlob field = Unsafe.As(GCHandle.FromIntPtr(fieldHandle).Target); + field.field.SetValue(fieldOwner, Marshal.PtrToStructure(valuePtr, field.field.FieldType)); } [UnmanagedCallersOnly] - internal static void FieldGetValue(IntPtr handle, IntPtr fieldHandle, IntPtr value_) + internal static void FieldGetValue(IntPtr fieldOwnerHandle, IntPtr fieldHandle, IntPtr valuePtr) { - var obj = GCHandle.FromIntPtr(handle).Target; - FieldInfo fieldInfo = (FieldInfo)GCHandle.FromIntPtr(fieldHandle).Target; - object fieldValue = fieldInfo.GetValue(obj); - - IntPtr value; - if (fieldValue is Type type) - value = GCHandle.ToIntPtr(GetOrAddTypeGCHandle(type)); - else if (fieldInfo.FieldType == typeof(IntPtr)) - value = (IntPtr)fieldValue; - else - value = GCHandle.ToIntPtr(GCHandle.Alloc(fieldValue, GCHandleType.Weak)); - Marshal.WriteIntPtr(value_, value); + object fieldOwner = GCHandle.FromIntPtr(fieldOwnerHandle).Target; + FieldBlob field = Unsafe.As(GCHandle.FromIntPtr(fieldHandle).Target); + field.toNativeMarshaller(field.field, fieldOwner, valuePtr, out int fieldOffset); } [UnmanagedCallersOnly] internal static void SetArrayValueReference(IntPtr arrayHandle, IntPtr elementPtr, IntPtr valueHandle) { - ManagedArray arr = (ManagedArray)GCHandle.FromIntPtr(arrayHandle).Target; - int index = (int)(elementPtr.ToInt64() - arr.PointerToPinnedArray.ToInt64()) / arr.elementSize; - arr.array.SetValue(valueHandle, index); + ManagedArray managedArray = Unsafe.As(GCHandle.FromIntPtr(arrayHandle).Target); + int index = (int)(elementPtr.ToInt64() - managedArray.PointerToPinnedArray.ToInt64()) / managedArray.ElementSize; + managedArray.SetValue(valueHandle, index); } [UnmanagedCallersOnly] @@ -1801,7 +2180,7 @@ namespace FlaxEngine [UnmanagedCallersOnly] internal static void CloseAssembly(IntPtr assemblyHandle) { - Assembly assembly = (Assembly)GCHandle.FromIntPtr(assemblyHandle).Target; + Assembly assembly = Unsafe.As(GCHandle.FromIntPtr(assemblyHandle).Target); GCHandle.FromIntPtr(assemblyHandle).Free(); assemblyHandles.Remove(assembly); @@ -1810,7 +2189,6 @@ namespace FlaxEngine // Clear all caches which might hold references to closing assembly cachedDelegatesCollectible.Clear(); typeCache.Clear(); - marshallableStructCache.Clear(); // Release all GCHandles in collectible ALC foreach (var pair in typeHandleCacheCollectible) @@ -1856,13 +2234,13 @@ namespace FlaxEngine [UnmanagedCallersOnly] internal static unsafe int NativeSizeOf(IntPtr typeHandle, uint* align) { - Type originalType = (Type)GCHandle.FromIntPtr(typeHandle).Target; - Type type = GetInternalType(originalType) ?? originalType; + Type type = Unsafe.As(GCHandle.FromIntPtr(typeHandle).Target); + Type nativeType = GetInternalType(type) ?? type; - if (type == typeof(Version)) - type = typeof(VersionNative); + if (nativeType == typeof(Version)) + nativeType = typeof(VersionNative); - int size = Marshal.SizeOf(type); + int size = Marshal.SizeOf(nativeType); *align = (uint)size; // Is this correct? return size; } @@ -1873,8 +2251,8 @@ namespace FlaxEngine if (typeHandle == othertypeHandle) return 1; - Type type = (Type)GCHandle.FromIntPtr(typeHandle).Target; - Type otherType = (Type)GCHandle.FromIntPtr(othertypeHandle).Target; + Type type = Unsafe.As(GCHandle.FromIntPtr(typeHandle).Target); + Type otherType = Unsafe.As(GCHandle.FromIntPtr(othertypeHandle).Target); if (type == otherType) return 1; @@ -1891,29 +2269,29 @@ namespace FlaxEngine [UnmanagedCallersOnly] internal static byte TypeIsValueType(IntPtr typeHandle) { - Type type = (Type)GCHandle.FromIntPtr(typeHandle).Target; + Type type = Unsafe.As(GCHandle.FromIntPtr(typeHandle).Target); return (byte)(type.IsValueType ? 1 : 0); } [UnmanagedCallersOnly] internal static byte TypeIsEnum(IntPtr typeHandle) { - Type type = (Type)GCHandle.FromIntPtr(typeHandle).Target; + Type type = Unsafe.As(GCHandle.FromIntPtr(typeHandle).Target); return (byte)(type.IsEnum ? 1 : 0); } [UnmanagedCallersOnly] internal static IntPtr GetClassParent(IntPtr typeHandle) { - Type type = (Type)GCHandle.FromIntPtr(typeHandle).Target; + Type type = Unsafe.As(GCHandle.FromIntPtr(typeHandle).Target); return GCHandle.ToIntPtr(GetTypeGCHandle(type.BaseType)); } [UnmanagedCallersOnly] internal static byte GetMethodParameterIsOut(IntPtr methodHandle, int parameterNum) { - (Type[] parameterTypes, MethodInfo methodInfo) = (Tuple)GCHandle.FromIntPtr(methodHandle).Target; - ParameterInfo parameterInfo = methodInfo.GetParameters()[parameterNum]; + MethodBlob methodBlob = Unsafe.As(GCHandle.FromIntPtr(methodHandle).Target); + ParameterInfo parameterInfo = methodBlob.method.GetParameters()[parameterNum]; return (byte)(parameterInfo.IsOut ? 1 : 0); } @@ -1955,16 +2333,16 @@ namespace FlaxEngine [UnmanagedCallersOnly] internal static IntPtr NewGCHandle(IntPtr ptr, byte pinned) { - object obj = GCHandle.FromIntPtr(ptr).Target; - GCHandle handle = GCHandle.Alloc(obj, pinned != 0 ? GCHandleType.Pinned : GCHandleType.Normal); + object value = GCHandle.FromIntPtr(ptr).Target; + GCHandle handle = GCHandle.Alloc(value, pinned != 0 ? GCHandleType.Pinned : GCHandleType.Normal); return GCHandle.ToIntPtr(handle); } [UnmanagedCallersOnly] internal static IntPtr NewGCHandleWeakref(IntPtr ptr, byte track_resurrection) { - object obj = GCHandle.FromIntPtr(ptr).Target; - GCHandle handle = GCHandle.Alloc(obj, track_resurrection != 0 ? GCHandleType.WeakTrackResurrection : GCHandleType.Weak); + object value = GCHandle.FromIntPtr(ptr).Target; + GCHandle handle = GCHandle.Alloc(value, track_resurrection != 0 ? GCHandleType.WeakTrackResurrection : GCHandleType.Weak); return GCHandle.ToIntPtr(handle); } @@ -2018,7 +2396,7 @@ namespace FlaxEngine [UnmanagedCallersOnly] internal static int GetTypeMonoTypeEnum(IntPtr typeHandle) { - Type type = (Type)GCHandle.FromIntPtr(typeHandle).Target; + Type type = Unsafe.As(GCHandle.FromIntPtr(typeHandle).Target); MonoType monoType; switch (type) @@ -2113,29 +2491,6 @@ namespace FlaxEngine // TODO: use MetadataReader to read types without loading any of the referenced assemblies? // https://makolyte.com/csharp-get-a-list-of-types-defined-in-an-assembly-without-loading-it/ - /*var assemblyPath = Utils.GetAssemblyLocation(assembly); - List types = new List(); - using (var sr = new StreamReader(assemblyPath)) - { - using (var portableExecutableReader = new PEReader(sr.BaseStream)) - { - var metadataReader = portableExecutableReader.GetMetadataReader(); - - foreach (var typeDefHandle in metadataReader.TypeDefinitions) - { - var typeDef = metadataReader.GetTypeDefinition(typeDefHandle); - - if (string.IsNullOrEmpty(metadataReader.GetString(typeDef.Namespace))) - continue; //if it's namespace is blank, it's not a user-defined type - - if (typeDef.Attributes.HasFlag(TypeAttributes.Abstract) || !typeDef.Attributes.HasFlag(TypeAttributes.Public)) - continue; //Not a internal concrete type - - types.Add(typeDef); - } - } - }*/ - // We need private types of this assembly too, DefinedTypes contains a lot of types from other assemblies... var types = referencedTypes.Any() ? assembly.DefinedTypes.Where(x => !referencedTypes.Contains(x.FullName)).ToArray() : assembly.DefinedTypes.ToArray(); @@ -2144,61 +2499,65 @@ namespace FlaxEngine return types; } + /// + /// Returns a static GCHandle for given Type, and caches it if needed. + /// internal static GCHandle GetOrAddTypeGCHandle(Type type) { - GCHandle handle; - if (typeHandleCache.TryGetValue(type.AssemblyQualifiedName, out handle)) + if (typeHandleCache.TryGetValue(type, out GCHandle handle)) return handle; - if (typeHandleCacheCollectible.TryGetValue(type.AssemblyQualifiedName, out handle)) + if (typeHandleCacheCollectible.TryGetValue(type, out handle)) return handle; handle = GCHandle.Alloc(type); if (type.IsCollectible) // check if generic parameters are also collectible? - typeHandleCacheCollectible.Add(type.AssemblyQualifiedName, handle); + typeHandleCacheCollectible.Add(type, handle); else - typeHandleCache.Add(type.AssemblyQualifiedName, handle); + typeHandleCache.Add(type, handle); return handle; } + /// + /// Returns a static GCHandle for given Type. + /// internal static GCHandle GetTypeGCHandle(Type type) { - GCHandle handle; - if (typeHandleCache.TryGetValue(type.AssemblyQualifiedName, out handle)) + if (typeHandleCache.TryGetValue(type, out GCHandle handle)) return handle; - if (typeHandleCacheCollectible.TryGetValue(type.AssemblyQualifiedName, out handle)) + if (typeHandleCacheCollectible.TryGetValue(type, out handle)) return handle; throw new Exception($"GCHandle not found for type '{type.FullName}'"); } - internal static class DelegateHelpers + private static class DelegateHelpers { - private static readonly Func MakeNewCustomDelegate = - (Func)Delegate.CreateDelegate(typeof(Func), - typeof(Expression).Assembly.GetType("System.Linq.Expressions.Compiler.DelegateHelpers") - .GetMethod("MakeNewCustomDelegate", BindingFlags.NonPublic | BindingFlags.Static)); + private static readonly Func MakeNewCustomDelegateFunc = + typeof(Expression).Assembly.GetType("System.Linq.Expressions.Compiler.DelegateHelpers") + .GetMethod("MakeNewCustomDelegate", BindingFlags.NonPublic | BindingFlags.Static) + .CreateDelegate>(); internal static void Init() { // Ensures the MakeNewCustomDelegate is put in the collectible ALC? using var ctx = scriptingAssemblyLoadContext.EnterContextualReflection(); - MakeNewCustomDelegate(new[] { typeof(void) }); + MakeNewCustomDelegateFunc(new[] { typeof(void) }); } - internal static Type NewDelegateType(params Type[] parameters) + internal static Type MakeNewCustomDelegate(Type[] parameters) { if (parameters.Any(x => scriptingAssemblyLoadContext.Assemblies.Contains(x.Module.Assembly))) { // Ensure the new delegate is placed in the collectible ALC //using var ctx = scriptingAssemblyLoadContext.EnterContextualReflection(); - return MakeNewCustomDelegate(parameters); + return MakeNewCustomDelegateFunc(parameters); } - return MakeNewCustomDelegate(parameters); + return MakeNewCustomDelegateFunc(parameters); } } @@ -2207,62 +2566,101 @@ namespace FlaxEngine /// internal class ThunkContext { - internal MethodInfo methodInfo; - internal int numParams; + internal MethodInfo method; internal Type[] parameterTypes; - internal object[] objParams; + internal Invoker.InvokeThunkDelegate methodDelegate; + internal object methodDelegateContext; - private static MethodInfo castMethod = typeof(ThunkContext).GetMethod("Cast", BindingFlags.Static | BindingFlags.NonPublic); - - private static object CastParameter(Type type, IntPtr ptr) + internal ThunkContext(MethodInfo method) { - if (type.IsValueType) + this.method = method; + parameterTypes = method.GetParameters().Select(x => x.ParameterType).ToArray(); + + List methodTypes = new List(); + if (!method.IsStatic) + methodTypes.Add(method.DeclaringType); + if (method.ReturnType != typeof(void)) + methodTypes.Add(method.ReturnType); + methodTypes.AddRange(parameterTypes); + + List genericParamTypes = new List(); + foreach (var type in methodTypes) { - var closeCast = castMethod.MakeGenericMethod(type); - return closeCast.Invoke(ptr, new[] { (object)ptr }); + if (type.IsByRef) + genericParamTypes.Add(type.GetElementType()); + else if (type.IsPointer) + genericParamTypes.Add(typeof(IntPtr)); + else + genericParamTypes.Add(type); } - else if (type.IsClass) - return ptr == IntPtr.Zero ? null : GCHandle.FromIntPtr(ptr).Target; - return null; + + string typeName = $"FlaxEngine.NativeInterop+Invoker+Invoker{(method.IsStatic ? "Static" : "")}{(method.ReturnType != typeof(void) ? "Ret" : "NoRet")}{parameterTypes.Length}{(genericParamTypes.Count > 0 ? "`" + genericParamTypes.Count : "")}"; + Type invokerType = genericParamTypes.Count == 0 ? Type.GetType(typeName) : Type.GetType(typeName).MakeGenericType(genericParamTypes.ToArray()); + methodDelegate = invokerType.GetMethod(nameof(Invoker.InvokerStaticNoRet0.InvokeThunk), BindingFlags.Static | BindingFlags.NonPublic).CreateDelegate(); + methodDelegateContext = invokerType.GetMethod(nameof(Invoker.InvokerStaticNoRet0.CreateInvokerDelegate), BindingFlags.Static | BindingFlags.NonPublic).Invoke(null, new object[] { method }); + + if (methodDelegate != null) + Assert.IsTrue(methodDelegateContext != null); } - public IntPtr InvokeThunk(IntPtr instance, IntPtr param1, IntPtr param2, IntPtr param3, IntPtr param4, IntPtr param5, IntPtr param6, IntPtr param7) + public IntPtr InvokeThunk(IntPtr instancePtr, IntPtr param1, IntPtr param2, IntPtr param3, IntPtr param4, IntPtr param5, IntPtr param6, IntPtr param7) { - var intPtrParams = stackalloc IntPtr[] { param1, param2, param3, param4, param5, param6, param7 }; - - for (int i = 0; i < numParams; i++) - objParams[i] = CastParameter(parameterTypes[i], intPtrParams[i]); - + IntPtr* nativePtrs = stackalloc IntPtr[] { param1, param2, param3, param4, param5, param6, param7 }; object returnObject; - try - { - returnObject = methodInfo.Invoke(instance != IntPtr.Zero ? GCHandle.FromIntPtr(instance).Target : null, objParams); - } - catch (Exception ex) - { - IntPtr exceptionPtr = intPtrParams[numParams]; - Marshal.WriteIntPtr(exceptionPtr, GCHandle.ToIntPtr(GCHandle.Alloc(ex, GCHandleType.Weak))); - return IntPtr.Zero; - } - // Marshal reference parameters - /*for (int i = 0; i < numParams; i++) + if (methodDelegate != null) { - Type parameterType = parameterTypes[i]; - if (parameterType.IsByRef) + try { - MarshalFromObject(parameterType.GetElementType(), objParams[i], intPtrParams[i], out int writeSize); + returnObject = methodDelegate(methodDelegateContext, instancePtr, nativePtrs); } - }*/ + catch (Exception exception) + { + if (Debugger.IsAttached && CatchExceptions) + throw; + + // Returned exception is the last parameter + IntPtr exceptionPtr = nativePtrs[parameterTypes.Length]; + Marshal.WriteIntPtr(exceptionPtr, GCHandle.ToIntPtr(GCHandle.Alloc(exception, GCHandleType.Weak))); + return IntPtr.Zero; + } + } + else + { + // The parameters are wrapped in GCHandles + int numParams = parameterTypes.Length; + object[] methodParameters = new object[numParams]; + for (int i = 0; i < numParams; i++) + methodParameters[i] = nativePtrs[i] == IntPtr.Zero ? null : GCHandle.FromIntPtr(nativePtrs[i]).Target; + + try + { + returnObject = method.Invoke(instancePtr != IntPtr.Zero ? GCHandle.FromIntPtr(instancePtr).Target : null, methodParameters); + } + catch (Exception exception) + { + if (Debugger.IsAttached && CatchExceptions) + throw; + + // Returned exception is the last parameter + IntPtr exceptionPtr = nativePtrs[numParams]; + Marshal.WriteIntPtr(exceptionPtr, GCHandle.ToIntPtr(GCHandle.Alloc(exception, GCHandleType.Weak))); + return IntPtr.Zero; + } + } if (returnObject is not null) { - if (returnObject is string returnStr) - return ManagedString.ToNative(returnStr); - else if (returnObject is IntPtr returnPtr) - return returnPtr; - else if (returnObject is bool boolValue) - return boolValue ? boolTruePtr : boolFalsePtr; + if (method.ReturnType == typeof(string)) + return ManagedString.ToNative(Unsafe.As(returnObject)); + else if (method.ReturnType == typeof(IntPtr)) + return (IntPtr)returnObject; + else if (method.ReturnType == typeof(bool)) + return (bool)returnObject ? boolTruePtr : boolFalsePtr; + else if (method.ReturnType == typeof(Type)) + return GCHandle.ToIntPtr(GetOrAddTypeGCHandle(Unsafe.As(returnObject))); + else if (method.ReturnType == typeof(object[])) + return GCHandle.ToIntPtr(GCHandle.Alloc(ManagedArray.Get(ManagedArrayToGCHandleArray(Unsafe.As(returnObject))), GCHandleType.Weak)); else return GCHandle.ToIntPtr(GCHandle.Alloc(returnObject, GCHandleType.Weak)); } diff --git a/Source/Engine/Engine/NativeInterop_Invoker.cs b/Source/Engine/Engine/NativeInterop_Invoker.cs new file mode 100644 index 000000000..5ca7bcc3c --- /dev/null +++ b/Source/Engine/Engine/NativeInterop_Invoker.cs @@ -0,0 +1,1055 @@ +#if USE_NETCORE + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; + +namespace FlaxEngine +{ + internal unsafe static partial class NativeInterop + { + internal static class Invoker + { + internal delegate object MarshalAndInvokeDelegate(object delegateContext, IntPtr instancePtr, IntPtr paramPtr); + internal delegate object InvokeThunkDelegate(object delegateContext, IntPtr instancePtr, IntPtr* paramPtrs); + + internal static T* ToPointer(IntPtr ptr) where T : unmanaged + { + return (T*)ptr.ToPointer(); + } + + /// + /// Creates a delegate for invoker to pass parameters as references. + /// + internal static Delegate CreateDelegateFromMethod(MethodInfo method, bool byRefParameters = true) + { + Type[] methodParameters = method.GetParameters().Select(x => x.ParameterType).ToArray(); + Type[] delegateParameters = method.GetParameters().Select(x => x.ParameterType.IsPointer ? typeof(IntPtr) : x.ParameterType).ToArray(); + if (byRefParameters) + delegateParameters = delegateParameters.Select(x => x.IsByRef ? x : x.MakeByRefType()).ToArray(); + + List parameterExpressions = new List(delegateParameters.Select(x => Expression.Parameter(x))); + List callExpressions = new List(parameterExpressions); + for (int i = 0; i < callExpressions.Count; i++) + if (methodParameters[i].IsPointer) + callExpressions[i] = Expression.Call(null, typeof(Invoker).GetMethod(nameof(Invoker.ToPointer), BindingFlags.Static | BindingFlags.NonPublic).MakeGenericMethod(methodParameters[i].GetElementType()), parameterExpressions[i]); + + var firstParamExp = Expression.Parameter(method.DeclaringType); + var callDelegExp = Expression.Call(!method.IsStatic ? firstParamExp : null, method, callExpressions.ToArray()); + + if (!method.IsStatic) + parameterExpressions.Insert(0, firstParamExp); + var lambda = Expression.Lambda(callDelegExp, parameterExpressions.ToArray()); + + return lambda.Compile(); + } + + + internal static class InvokerNoRet0 + { + internal delegate void InvokerDelegate(object instance); + internal delegate void ThunkInvokerDelegate(object instance); + + internal static object CreateDelegate(MethodInfo method) + { + return new Tuple(method.GetParameters().Select(x => x.ParameterType).ToArray(), Unsafe.As(CreateDelegateFromMethod(method))); + } + + internal static object CreateInvokerDelegate(MethodInfo method) + { + return Unsafe.As(CreateDelegateFromMethod(method, false)); + } + + internal static object MarshalAndInvoke(object delegateContext, IntPtr instancePtr, IntPtr paramPtr) + { + (Type[] types, InvokerDelegate deleg) = (Tuple)(delegateContext); + + deleg(GCHandle.FromIntPtr(instancePtr).Target); + + return null; + } + + internal static unsafe object InvokeThunk(object delegateContext, IntPtr instancePtr, IntPtr* paramPtrs) + { + ThunkInvokerDelegate deleg = Unsafe.As(delegateContext); + + deleg(GCHandle.FromIntPtr(instancePtr).Target); + + return null; + } + } + + internal static class InvokerNoRet1 + { + internal delegate void InvokerDelegate(object instance, ref T1 param1); + internal delegate void ThunkInvokerDelegate(object instance, T1 param1); + + internal static object CreateDelegate(MethodInfo method) + { + return new Tuple(method.GetParameters().Select(x => x.ParameterType).ToArray(), Unsafe.As(CreateDelegateFromMethod(method))); + } + + internal static object CreateInvokerDelegate(MethodInfo method) + { + return Unsafe.As(CreateDelegateFromMethod(method, false)); + } + + internal static object MarshalAndInvoke(object delegateContext, IntPtr instancePtr, IntPtr paramPtr) + { + (Type[] types, InvokerDelegate deleg) = (Tuple)(delegateContext); + + IntPtr param1Ptr = Marshal.ReadIntPtr(paramPtr); + + T1 param1 = default; + if (param1Ptr != IntPtr.Zero) MarshalHelper.ToManaged(ref param1, param1Ptr, types[0].IsByRef); + + deleg(GCHandle.FromIntPtr(instancePtr).Target, ref param1); + + // Marshal reference parameters back to original unmanaged references + if (types[0].IsByRef) + MarshalHelper.ToNative(ref param1, param1Ptr); + + return null; + } + + internal static unsafe object InvokeThunk(object delegateContext, IntPtr instancePtr, IntPtr* paramPtrs) + { + ThunkInvokerDelegate deleg = Unsafe.As(delegateContext); + + T1 param1 = paramPtrs[0] != IntPtr.Zero ? (T1)GCHandle.FromIntPtr(paramPtrs[0]).Target : default(T1); + + deleg(GCHandle.FromIntPtr(instancePtr).Target, param1); + + return null; + } + } + + internal static class InvokerNoRet2 + { + internal delegate void InvokerDelegate(object instance, ref T1 param1, ref T2 param2); + internal delegate void ThunkInvokerDelegate(object instance, T1 param1, T2 param2); + + internal static object CreateDelegate(MethodInfo method) + { + return new Tuple(method.GetParameters().Select(x => x.ParameterType).ToArray(), Unsafe.As(CreateDelegateFromMethod(method))); + } + + internal static object CreateInvokerDelegate(MethodInfo method) + { + return Unsafe.As(CreateDelegateFromMethod(method, false)); + } + + internal static object MarshalAndInvoke(object delegateContext, IntPtr instancePtr, IntPtr paramPtr) + { + (Type[] types, InvokerDelegate deleg) = (Tuple)(delegateContext); + + IntPtr param1Ptr = Marshal.ReadIntPtr(paramPtr); + IntPtr param2Ptr = Marshal.ReadIntPtr(paramPtr + IntPtr.Size); + + T1 param1 = default; + T2 param2 = default; + if (param1Ptr != IntPtr.Zero) MarshalHelper.ToManaged(ref param1, param1Ptr, types[0].IsByRef); + if (param2Ptr != IntPtr.Zero) MarshalHelper.ToManaged(ref param2, param2Ptr, types[1].IsByRef); + + deleg(GCHandle.FromIntPtr(instancePtr).Target, ref param1, ref param2); + + // Marshal reference parameters back to original unmanaged references + if (types[0].IsByRef) + MarshalHelper.ToNative(ref param1, param1Ptr); + if (types[1].IsByRef) + MarshalHelper.ToNative(ref param2, param2Ptr); + + return null; + } + + internal static unsafe object InvokeThunk(object delegateContext, IntPtr instancePtr, IntPtr* paramPtrs) + { + ThunkInvokerDelegate deleg = Unsafe.As(delegateContext); + + T1 param1 = paramPtrs[0] != IntPtr.Zero ? (T1)GCHandle.FromIntPtr(paramPtrs[0]).Target : default(T1); + T2 param2 = paramPtrs[1] != IntPtr.Zero ? (T2)GCHandle.FromIntPtr(paramPtrs[1]).Target : default(T2); + + deleg(GCHandle.FromIntPtr(instancePtr).Target, param1, param2); + + return null; + } + } + + internal static class InvokerNoRet3 + { + internal delegate void InvokerDelegate(object instance, ref T1 param1, ref T2 param2, ref T3 param3); + internal delegate void ThunkInvokerDelegate(object instance, T1 param1, T2 param2, T3 param3); + + internal static object CreateDelegate(MethodInfo method) + { + return new Tuple(method.GetParameters().Select(x => x.ParameterType).ToArray(), Unsafe.As(CreateDelegateFromMethod(method))); + } + + internal static object CreateInvokerDelegate(MethodInfo method) + { + return Unsafe.As(CreateDelegateFromMethod(method, false)); + } + + internal static object MarshalAndInvoke(object delegateContext, IntPtr instancePtr, IntPtr paramPtr) + { + (Type[] types, InvokerDelegate deleg) = (Tuple)(delegateContext); + + IntPtr param1Ptr = Marshal.ReadIntPtr(paramPtr); + IntPtr param2Ptr = Marshal.ReadIntPtr(paramPtr + IntPtr.Size); + IntPtr param3Ptr = Marshal.ReadIntPtr(paramPtr + IntPtr.Size + IntPtr.Size); + + T1 param1 = default; + T2 param2 = default; + T3 param3 = default; + if (param1Ptr != IntPtr.Zero) MarshalHelper.ToManaged(ref param1, param1Ptr, types[0].IsByRef); + if (param2Ptr != IntPtr.Zero) MarshalHelper.ToManaged(ref param2, param2Ptr, types[1].IsByRef); + if (param3Ptr != IntPtr.Zero) MarshalHelper.ToManaged(ref param3, param3Ptr, types[2].IsByRef); + + deleg(GCHandle.FromIntPtr(instancePtr).Target, ref param1, ref param2, ref param3); + + // Marshal reference parameters back to original unmanaged references + if (types[0].IsByRef) + MarshalHelper.ToNative(ref param1, param1Ptr); + if (types[1].IsByRef) + MarshalHelper.ToNative(ref param2, param2Ptr); + if (types[2].IsByRef) + MarshalHelper.ToNative(ref param3, param3Ptr); + + return null; + } + + internal static unsafe object InvokeThunk(object delegateContext, IntPtr instancePtr, IntPtr* paramPtrs) + { + ThunkInvokerDelegate deleg = Unsafe.As(delegateContext); + + T1 param1 = paramPtrs[0] != IntPtr.Zero ? (T1)GCHandle.FromIntPtr(paramPtrs[0]).Target : default(T1); + T2 param2 = paramPtrs[1] != IntPtr.Zero ? (T2)GCHandle.FromIntPtr(paramPtrs[1]).Target : default(T2); + T3 param3 = paramPtrs[2] != IntPtr.Zero ? (T3)GCHandle.FromIntPtr(paramPtrs[2]).Target : default(T3); + + deleg(GCHandle.FromIntPtr(instancePtr).Target, param1, param2, param3); + + return null; + } + } + + internal static class InvokerNoRet4 + { + internal delegate void InvokerDelegate(object instance, ref T1 param1, ref T2 param2, ref T3 param3, ref T4 param4); + internal delegate void ThunkInvokerDelegate(object instance, T1 param1, T2 param2, T3 param3, T4 param4); + + internal static object CreateDelegate(MethodInfo method) + { + return new Tuple(method.GetParameters().Select(x => x.ParameterType).ToArray(), Unsafe.As(CreateDelegateFromMethod(method))); + } + + internal static object CreateInvokerDelegate(MethodInfo method) + { + return Unsafe.As(CreateDelegateFromMethod(method, false)); + } + + internal static object MarshalAndInvoke(object delegateContext, IntPtr instancePtr, IntPtr paramPtr) + { + (Type[] types, InvokerDelegate deleg) = (Tuple)(delegateContext); + + IntPtr param1Ptr = Marshal.ReadIntPtr(paramPtr); + IntPtr param2Ptr = Marshal.ReadIntPtr(paramPtr + IntPtr.Size); + IntPtr param3Ptr = Marshal.ReadIntPtr(paramPtr + IntPtr.Size + IntPtr.Size); + IntPtr param4Ptr = Marshal.ReadIntPtr(paramPtr + IntPtr.Size + IntPtr.Size + IntPtr.Size); + + T1 param1 = default; + T2 param2 = default; + T3 param3 = default; + T4 param4 = default; + if (param1Ptr != IntPtr.Zero) MarshalHelper.ToManaged(ref param1, param1Ptr, types[0].IsByRef); + if (param2Ptr != IntPtr.Zero) MarshalHelper.ToManaged(ref param2, param2Ptr, types[1].IsByRef); + if (param3Ptr != IntPtr.Zero) MarshalHelper.ToManaged(ref param3, param3Ptr, types[2].IsByRef); + if (param4Ptr != IntPtr.Zero) MarshalHelper.ToManaged(ref param4, param4Ptr, types[3].IsByRef); + + deleg(GCHandle.FromIntPtr(instancePtr).Target, ref param1, ref param2, ref param3, ref param4); + + // Marshal reference parameters back to original unmanaged references + if (types[0].IsByRef) + MarshalHelper.ToNative(ref param1, param1Ptr); + if (types[1].IsByRef) + MarshalHelper.ToNative(ref param2, param2Ptr); + if (types[2].IsByRef) + MarshalHelper.ToNative(ref param3, param3Ptr); + if (types[3].IsByRef) + MarshalHelper.ToNative(ref param4, param4Ptr); + + return null; + } + + internal static unsafe object InvokeThunk(object delegateContext, IntPtr instancePtr, IntPtr* paramPtrs) + { + ThunkInvokerDelegate deleg = Unsafe.As(delegateContext); + + T1 param1 = paramPtrs[0] != IntPtr.Zero ? (T1)GCHandle.FromIntPtr(paramPtrs[0]).Target : default(T1); + T2 param2 = paramPtrs[1] != IntPtr.Zero ? (T2)GCHandle.FromIntPtr(paramPtrs[1]).Target : default(T2); + T3 param3 = paramPtrs[2] != IntPtr.Zero ? (T3)GCHandle.FromIntPtr(paramPtrs[2]).Target : default(T3); + T4 param4 = paramPtrs[3] != IntPtr.Zero ? (T4)GCHandle.FromIntPtr(paramPtrs[3]).Target : default(T4); + + deleg(GCHandle.FromIntPtr(instancePtr).Target, param1, param2, param3, param4); + + return null; + } + } + + internal static class InvokerStaticNoRet0 + { + internal delegate void InvokerDelegate(); + internal delegate void ThunkInvokerDelegate(); + + internal static object CreateDelegate(MethodInfo method) + { + return new Tuple(method.GetParameters().Select(x => x.ParameterType).ToArray(), Unsafe.As(CreateDelegateFromMethod(method))); + } + + internal static object CreateInvokerDelegate(MethodInfo method) + { + return Unsafe.As(CreateDelegateFromMethod(method, false)); + } + + internal static object MarshalAndInvoke(object delegateContext, IntPtr instancePtr, IntPtr paramPtr) + { + (Type[] types, InvokerDelegate deleg) = (Tuple)(delegateContext); + + deleg(); + + return null; + } + + internal static unsafe object InvokeThunk(object delegateContext, IntPtr instancePtr, IntPtr* paramPtrs) + { + ThunkInvokerDelegate deleg = Unsafe.As(delegateContext); + + deleg(); + + return null; + } + } + + internal static class InvokerStaticNoRet1 + { + internal delegate void InvokerDelegate(ref T1 param1); + internal delegate void ThunkInvokerDelegate(T1 param1); + + internal static object CreateDelegate(MethodInfo method) + { + return new Tuple(method.GetParameters().Select(x => x.ParameterType).ToArray(), Unsafe.As(CreateDelegateFromMethod(method))); + } + + internal static object CreateInvokerDelegate(MethodInfo method) + { + return Unsafe.As(CreateDelegateFromMethod(method, false)); + } + + internal static object MarshalAndInvoke(object delegateContext, IntPtr instancePtr, IntPtr paramPtr) + { + (Type[] types, InvokerDelegate deleg) = (Tuple)(delegateContext); + + IntPtr param1Ptr = Marshal.ReadIntPtr(paramPtr); + + T1 param1 = default; + if (param1Ptr != IntPtr.Zero) MarshalHelper.ToManaged(ref param1, param1Ptr, types[0].IsByRef); + + deleg(ref param1); + + // Marshal reference parameters back to original unmanaged references + if (types[0].IsByRef) + MarshalHelper.ToNative(ref param1, param1Ptr); + + return null; + } + + internal static unsafe object InvokeThunk(object delegateContext, IntPtr instancePtr, IntPtr* paramPtrs) + { + ThunkInvokerDelegate deleg = Unsafe.As(delegateContext); + + T1 param1 = paramPtrs[0] != IntPtr.Zero ? (T1)GCHandle.FromIntPtr(paramPtrs[0]).Target : default(T1); + + deleg(param1); + + return null; + } + } + + internal static class InvokerStaticNoRet2 + { + internal delegate void InvokerDelegate(ref T1 param1, ref T2 param2); + internal delegate void ThunkInvokerDelegate(T1 param1, T2 param2); + + internal static object CreateDelegate(MethodInfo method) + { + return new Tuple(method.GetParameters().Select(x => x.ParameterType).ToArray(), Unsafe.As(CreateDelegateFromMethod(method))); + } + + internal static object CreateInvokerDelegate(MethodInfo method) + { + return Unsafe.As(CreateDelegateFromMethod(method, false)); + } + + internal static object MarshalAndInvoke(object delegateContext, IntPtr instancePtr, IntPtr paramPtr) + { + (Type[] types, InvokerDelegate deleg) = (Tuple)(delegateContext); + + IntPtr param1Ptr = Marshal.ReadIntPtr(paramPtr); + IntPtr param2Ptr = Marshal.ReadIntPtr(paramPtr + IntPtr.Size); + + T1 param1 = default; + T2 param2 = default; + if (param1Ptr != IntPtr.Zero) MarshalHelper.ToManaged(ref param1, param1Ptr, types[0].IsByRef); + if (param2Ptr != IntPtr.Zero) MarshalHelper.ToManaged(ref param2, param2Ptr, types[1].IsByRef); + + deleg(ref param1, ref param2); + + // Marshal reference parameters back to original unmanaged references + if (types[0].IsByRef) + MarshalHelper.ToNative(ref param1, param1Ptr); + if (types[1].IsByRef) + MarshalHelper.ToNative(ref param2, param2Ptr); + + return null; + } + + internal static unsafe object InvokeThunk(object delegateContext, IntPtr instancePtr, IntPtr* paramPtrs) + { + ThunkInvokerDelegate deleg = Unsafe.As(delegateContext); + + T1 param1 = paramPtrs[0] != IntPtr.Zero ? (T1)GCHandle.FromIntPtr(paramPtrs[0]).Target : default(T1); + T2 param2 = paramPtrs[1] != IntPtr.Zero ? (T2)GCHandle.FromIntPtr(paramPtrs[1]).Target : default(T2); + + deleg(param1, param2); + + return null; + } + } + + internal static class InvokerStaticNoRet3 + { + internal delegate void InvokerDelegate(ref T1 param1, ref T2 param2, ref T3 param3); + internal delegate void ThunkInvokerDelegate(T1 param1, T2 param2, T3 param3); + + internal static object CreateDelegate(MethodInfo method) + { + return new Tuple(method.GetParameters().Select(x => x.ParameterType).ToArray(), Unsafe.As(CreateDelegateFromMethod(method))); + } + + internal static object CreateInvokerDelegate(MethodInfo method) + { + return Unsafe.As(CreateDelegateFromMethod(method, false)); + } + + internal static object MarshalAndInvoke(object delegateContext, IntPtr instancePtr, IntPtr paramPtr) + { + (Type[] types, InvokerDelegate deleg) = (Tuple)(delegateContext); + + IntPtr param1Ptr = Marshal.ReadIntPtr(paramPtr); + IntPtr param2Ptr = Marshal.ReadIntPtr(paramPtr + IntPtr.Size); + IntPtr param3Ptr = Marshal.ReadIntPtr(paramPtr + IntPtr.Size + IntPtr.Size); + + T1 param1 = default; + T2 param2 = default; + T3 param3 = default; + if (param1Ptr != IntPtr.Zero) MarshalHelper.ToManaged(ref param1, param1Ptr, types[0].IsByRef); + if (param2Ptr != IntPtr.Zero) MarshalHelper.ToManaged(ref param2, param2Ptr, types[1].IsByRef); + if (param3Ptr != IntPtr.Zero) MarshalHelper.ToManaged(ref param3, param3Ptr, types[2].IsByRef); + + deleg(ref param1, ref param2, ref param3); + + // Marshal reference parameters back to original unmanaged references + if (types[0].IsByRef) + MarshalHelper.ToNative(ref param1, param1Ptr); + if (types[1].IsByRef) + MarshalHelper.ToNative(ref param2, param2Ptr); + if (types[2].IsByRef) + MarshalHelper.ToNative(ref param3, param3Ptr); + + return null; + } + + internal static unsafe object InvokeThunk(object delegateContext, IntPtr instancePtr, IntPtr* paramPtrs) + { + ThunkInvokerDelegate deleg = Unsafe.As(delegateContext); + + T1 param1 = paramPtrs[0] != IntPtr.Zero ? (T1)GCHandle.FromIntPtr(paramPtrs[0]).Target : default(T1); + T2 param2 = paramPtrs[1] != IntPtr.Zero ? (T2)GCHandle.FromIntPtr(paramPtrs[1]).Target : default(T2); + T3 param3 = paramPtrs[2] != IntPtr.Zero ? (T3)GCHandle.FromIntPtr(paramPtrs[2]).Target : default(T3); + + deleg(param1, param2, param3); + + return null; + } + } + + internal static class InvokerStaticNoRet4 + { + internal delegate void InvokerDelegate(ref T1 param1, ref T2 param2, ref T3 param3, ref T4 param4); + internal delegate void ThunkInvokerDelegate(T1 param1, T2 param2, T3 param3, T4 param4); + + internal static object CreateDelegate(MethodInfo method) + { + return new Tuple(method.GetParameters().Select(x => x.ParameterType).ToArray(), Unsafe.As(CreateDelegateFromMethod(method))); + } + + internal static object CreateInvokerDelegate(MethodInfo method) + { + return Unsafe.As(CreateDelegateFromMethod(method, false)); + } + + internal static object MarshalAndInvoke(object delegateContext, IntPtr instancePtr, IntPtr paramPtr) + { + (Type[] types, InvokerDelegate deleg) = (Tuple)(delegateContext); + + IntPtr param1Ptr = Marshal.ReadIntPtr(paramPtr); + IntPtr param2Ptr = Marshal.ReadIntPtr(paramPtr + IntPtr.Size); + IntPtr param3Ptr = Marshal.ReadIntPtr(paramPtr + IntPtr.Size + IntPtr.Size); + IntPtr param4Ptr = Marshal.ReadIntPtr(paramPtr + IntPtr.Size + IntPtr.Size + IntPtr.Size); + + T1 param1 = default; + T2 param2 = default; + T3 param3 = default; + T4 param4 = default; + if (param1Ptr != IntPtr.Zero) MarshalHelper.ToManaged(ref param1, param1Ptr, types[0].IsByRef); + if (param2Ptr != IntPtr.Zero) MarshalHelper.ToManaged(ref param2, param2Ptr, types[1].IsByRef); + if (param3Ptr != IntPtr.Zero) MarshalHelper.ToManaged(ref param3, param3Ptr, types[2].IsByRef); + if (param4Ptr != IntPtr.Zero) MarshalHelper.ToManaged(ref param4, param4Ptr, types[3].IsByRef); + + deleg(ref param1, ref param2, ref param3, ref param4); + + // Marshal reference parameters back to original unmanaged references + if (types[0].IsByRef) + MarshalHelper.ToNative(ref param1, param1Ptr); + if (types[1].IsByRef) + MarshalHelper.ToNative(ref param2, param2Ptr); + if (types[2].IsByRef) + MarshalHelper.ToNative(ref param3, param3Ptr); + if (types[3].IsByRef) + MarshalHelper.ToNative(ref param4, param4Ptr); + + return null; + } + + internal static unsafe object InvokeThunk(object delegateContext, IntPtr instancePtr, IntPtr* paramPtrs) + { + ThunkInvokerDelegate deleg = Unsafe.As(delegateContext); + + T1 param1 = paramPtrs[0] != IntPtr.Zero ? (T1)GCHandle.FromIntPtr(paramPtrs[0]).Target : default(T1); + T2 param2 = paramPtrs[1] != IntPtr.Zero ? (T2)GCHandle.FromIntPtr(paramPtrs[1]).Target : default(T2); + T3 param3 = paramPtrs[2] != IntPtr.Zero ? (T3)GCHandle.FromIntPtr(paramPtrs[2]).Target : default(T3); + T4 param4 = paramPtrs[3] != IntPtr.Zero ? (T4)GCHandle.FromIntPtr(paramPtrs[3]).Target : default(T4); + + deleg(param1, param2, param3, param4); + + return null; + } + } + + internal static class InvokerRet0 + { + internal delegate TRet InvokerDelegate(object instance); + internal delegate TRet ThunkInvokerDelegate(object instance); + + internal static object CreateDelegate(MethodInfo method) + { + return new Tuple(method.GetParameters().Select(x => x.ParameterType).ToArray(), Unsafe.As(CreateDelegateFromMethod(method))); + } + + internal static object CreateInvokerDelegate(MethodInfo method) + { + return Unsafe.As(CreateDelegateFromMethod(method, false)); + } + + internal static object MarshalAndInvoke(object delegateContext, IntPtr instancePtr, IntPtr paramPtr) + { + (Type[] types, InvokerDelegate deleg) = (Tuple)(delegateContext); + + TRet ret = deleg(GCHandle.FromIntPtr(instancePtr).Target); + + return ret; + } + + internal static unsafe object InvokeThunk(object delegateContext, IntPtr instancePtr, IntPtr* paramPtrs) + { + ThunkInvokerDelegate deleg = Unsafe.As(delegateContext); + + TRet ret = deleg(GCHandle.FromIntPtr(instancePtr).Target); + + return ret; + } + } + + internal static class InvokerRet1 + { + internal delegate TRet InvokerDelegate(object instance, ref T1 param1); + internal delegate TRet ThunkInvokerDelegate(object instance, T1 param1); + + internal static object CreateDelegate(MethodInfo method) + { + return new Tuple(method.GetParameters().Select(x => x.ParameterType).ToArray(), Unsafe.As(CreateDelegateFromMethod(method))); + } + + internal static object CreateInvokerDelegate(MethodInfo method) + { + return Unsafe.As(CreateDelegateFromMethod(method, false)); + } + + internal static object MarshalAndInvoke(object delegateContext, IntPtr instancePtr, IntPtr paramPtr) + { + (Type[] types, InvokerDelegate deleg) = (Tuple)(delegateContext); + + IntPtr param1Ptr = Marshal.ReadIntPtr(paramPtr); + + T1 param1 = default; + if (param1Ptr != IntPtr.Zero) MarshalHelper.ToManaged(ref param1, param1Ptr, types[0].IsByRef); + + TRet ret = deleg(GCHandle.FromIntPtr(instancePtr).Target, ref param1); + + // Marshal reference parameters back to original unmanaged references + if (types[0].IsByRef) + MarshalHelper.ToNative(ref param1, param1Ptr); + + return ret; + } + + internal static unsafe object InvokeThunk(object delegateContext, IntPtr instancePtr, IntPtr* paramPtrs) + { + ThunkInvokerDelegate deleg = Unsafe.As(delegateContext); + + T1 param1 = paramPtrs[0] != IntPtr.Zero ? (T1)GCHandle.FromIntPtr(paramPtrs[0]).Target : default(T1); + + TRet ret = deleg(GCHandle.FromIntPtr(instancePtr).Target, param1); + + return ret; + } + } + + internal static class InvokerRet2 + { + internal delegate TRet InvokerDelegate(object instance, ref T1 param1, ref T2 param2); + internal delegate TRet ThunkInvokerDelegate(object instance, T1 param1, T2 param2); + + internal static object CreateDelegate(MethodInfo method) + { + return new Tuple(method.GetParameters().Select(x => x.ParameterType).ToArray(), Unsafe.As(CreateDelegateFromMethod(method))); + } + + internal static object CreateInvokerDelegate(MethodInfo method) + { + return Unsafe.As(CreateDelegateFromMethod(method, false)); + } + + internal static object MarshalAndInvoke(object delegateContext, IntPtr instancePtr, IntPtr paramPtr) + { + (Type[] types, InvokerDelegate deleg) = (Tuple)(delegateContext); + + IntPtr param1Ptr = Marshal.ReadIntPtr(paramPtr); + IntPtr param2Ptr = Marshal.ReadIntPtr(paramPtr + IntPtr.Size); + + T1 param1 = default; + T2 param2 = default; + if (param1Ptr != IntPtr.Zero) MarshalHelper.ToManaged(ref param1, param1Ptr, types[0].IsByRef); + if (param2Ptr != IntPtr.Zero) MarshalHelper.ToManaged(ref param2, param2Ptr, types[1].IsByRef); + + TRet ret = deleg(GCHandle.FromIntPtr(instancePtr).Target, ref param1, ref param2); + + // Marshal reference parameters back to original unmanaged references + if (types[0].IsByRef) + MarshalHelper.ToNative(ref param1, param1Ptr); + if (types[1].IsByRef) + MarshalHelper.ToNative(ref param2, param2Ptr); + + return ret; + } + + internal static unsafe object InvokeThunk(object delegateContext, IntPtr instancePtr, IntPtr* paramPtrs) + { + ThunkInvokerDelegate deleg = Unsafe.As(delegateContext); + + T1 param1 = paramPtrs[0] != IntPtr.Zero ? (T1)GCHandle.FromIntPtr(paramPtrs[0]).Target : default(T1); + T2 param2 = paramPtrs[1] != IntPtr.Zero ? (T2)GCHandle.FromIntPtr(paramPtrs[1]).Target : default(T2); + + TRet ret = deleg(GCHandle.FromIntPtr(instancePtr).Target, param1, param2); + + return ret; + } + } + + internal static class InvokerRet3 + { + internal delegate TRet InvokerDelegate(object instance, ref T1 param1, ref T2 param2, ref T3 param3); + internal delegate TRet ThunkInvokerDelegate(object instance, T1 param1, T2 param2, T3 param3); + + internal static object CreateDelegate(MethodInfo method) + { + return new Tuple(method.GetParameters().Select(x => x.ParameterType).ToArray(), Unsafe.As(CreateDelegateFromMethod(method))); + } + + internal static object CreateInvokerDelegate(MethodInfo method) + { + return Unsafe.As(CreateDelegateFromMethod(method, false)); + } + + internal static object MarshalAndInvoke(object delegateContext, IntPtr instancePtr, IntPtr paramPtr) + { + (Type[] types, InvokerDelegate deleg) = (Tuple)(delegateContext); + + IntPtr param1Ptr = Marshal.ReadIntPtr(paramPtr); + IntPtr param2Ptr = Marshal.ReadIntPtr(paramPtr + IntPtr.Size); + IntPtr param3Ptr = Marshal.ReadIntPtr(paramPtr + IntPtr.Size + IntPtr.Size); + + T1 param1 = default; + T2 param2 = default; + T3 param3 = default; + if (param1Ptr != IntPtr.Zero) MarshalHelper.ToManaged(ref param1, param1Ptr, types[0].IsByRef); + if (param2Ptr != IntPtr.Zero) MarshalHelper.ToManaged(ref param2, param2Ptr, types[1].IsByRef); + if (param3Ptr != IntPtr.Zero) MarshalHelper.ToManaged(ref param3, param3Ptr, types[2].IsByRef); + + TRet ret = deleg(GCHandle.FromIntPtr(instancePtr).Target, ref param1, ref param2, ref param3); + + // Marshal reference parameters back to original unmanaged references + if (types[0].IsByRef) + MarshalHelper.ToNative(ref param1, param1Ptr); + if (types[1].IsByRef) + MarshalHelper.ToNative(ref param2, param2Ptr); + if (types[2].IsByRef) + MarshalHelper.ToNative(ref param3, param3Ptr); + + return ret; + } + + internal static unsafe object InvokeThunk(object delegateContext, IntPtr instancePtr, IntPtr* paramPtrs) + { + ThunkInvokerDelegate deleg = Unsafe.As(delegateContext); + + T1 param1 = paramPtrs[0] != IntPtr.Zero ? (T1)GCHandle.FromIntPtr(paramPtrs[0]).Target : default(T1); + T2 param2 = paramPtrs[1] != IntPtr.Zero ? (T2)GCHandle.FromIntPtr(paramPtrs[1]).Target : default(T2); + T3 param3 = paramPtrs[2] != IntPtr.Zero ? (T3)GCHandle.FromIntPtr(paramPtrs[2]).Target : default(T3); + + TRet ret = deleg(GCHandle.FromIntPtr(instancePtr).Target, param1, param2, param3); + + return ret; + } + } + + internal static class InvokerRet4 + { + internal delegate TRet InvokerDelegate(object instance, ref T1 param1, ref T2 param2, ref T3 param3, ref T4 param4); + internal delegate TRet ThunkInvokerDelegate(object instance, T1 param1, T2 param2, T3 param3, T4 param4); + + internal static object CreateDelegate(MethodInfo method) + { + return new Tuple(method.GetParameters().Select(x => x.ParameterType).ToArray(), Unsafe.As(CreateDelegateFromMethod(method))); + } + + internal static object CreateInvokerDelegate(MethodInfo method) + { + return Unsafe.As(CreateDelegateFromMethod(method, false)); + } + + internal static object MarshalAndInvoke(object delegateContext, IntPtr instancePtr, IntPtr paramPtr) + { + (Type[] types, InvokerDelegate deleg) = (Tuple)(delegateContext); + + IntPtr param1Ptr = Marshal.ReadIntPtr(paramPtr); + IntPtr param2Ptr = Marshal.ReadIntPtr(paramPtr + IntPtr.Size); + IntPtr param3Ptr = Marshal.ReadIntPtr(paramPtr + IntPtr.Size + IntPtr.Size); + IntPtr param4Ptr = Marshal.ReadIntPtr(paramPtr + IntPtr.Size + IntPtr.Size + IntPtr.Size); + + T1 param1 = default; + T2 param2 = default; + T3 param3 = default; + T4 param4 = default; + if (param1Ptr != IntPtr.Zero) MarshalHelper.ToManaged(ref param1, param1Ptr, types[0].IsByRef); + if (param2Ptr != IntPtr.Zero) MarshalHelper.ToManaged(ref param2, param2Ptr, types[1].IsByRef); + if (param3Ptr != IntPtr.Zero) MarshalHelper.ToManaged(ref param3, param3Ptr, types[2].IsByRef); + if (param4Ptr != IntPtr.Zero) MarshalHelper.ToManaged(ref param4, param4Ptr, types[3].IsByRef); + + TRet ret = deleg(GCHandle.FromIntPtr(instancePtr).Target, ref param1, ref param2, ref param3, ref param4); + + // Marshal reference parameters back to original unmanaged references + if (types[0].IsByRef) + MarshalHelper.ToNative(ref param1, param1Ptr); + if (types[1].IsByRef) + MarshalHelper.ToNative(ref param2, param2Ptr); + if (types[2].IsByRef) + MarshalHelper.ToNative(ref param3, param3Ptr); + if (types[3].IsByRef) + MarshalHelper.ToNative(ref param4, param4Ptr); + + return ret; + } + + internal static unsafe object InvokeThunk(object delegateContext, IntPtr instancePtr, IntPtr* paramPtrs) + { + ThunkInvokerDelegate deleg = Unsafe.As(delegateContext); + + T1 param1 = paramPtrs[0] != IntPtr.Zero ? (T1)GCHandle.FromIntPtr(paramPtrs[0]).Target : default(T1); + T2 param2 = paramPtrs[1] != IntPtr.Zero ? (T2)GCHandle.FromIntPtr(paramPtrs[1]).Target : default(T2); + T3 param3 = paramPtrs[2] != IntPtr.Zero ? (T3)GCHandle.FromIntPtr(paramPtrs[2]).Target : default(T3); + T4 param4 = paramPtrs[3] != IntPtr.Zero ? (T4)GCHandle.FromIntPtr(paramPtrs[3]).Target : default(T4); + + TRet ret = deleg(GCHandle.FromIntPtr(instancePtr).Target, param1, param2, param3, param4); + + return ret; + } + } + + internal static class InvokerStaticRet0 + { + internal delegate TRet InvokerDelegate(); + internal delegate TRet ThunkInvokerDelegate(); + + internal static object CreateDelegate(MethodInfo method) + { + return new Tuple(method.GetParameters().Select(x => x.ParameterType).ToArray(), Unsafe.As(CreateDelegateFromMethod(method))); + } + + internal static object CreateInvokerDelegate(MethodInfo method) + { + return Unsafe.As(CreateDelegateFromMethod(method, false)); + } + + internal static object MarshalAndInvoke(object delegateContext, IntPtr instancePtr, IntPtr paramPtr) + { + (Type[] types, InvokerDelegate deleg) = (Tuple)(delegateContext); + + TRet ret = deleg(); + + return ret; + } + + internal static unsafe object InvokeThunk(object delegateContext, IntPtr instancePtr, IntPtr* paramPtrs) + { + ThunkInvokerDelegate deleg = Unsafe.As(delegateContext); + + TRet ret = deleg(); + + return ret; + } + } + + internal static class InvokerStaticRet1 + { + internal delegate TRet InvokerDelegate(ref T1 param1); + internal delegate TRet ThunkInvokerDelegate(T1 param1); + + internal static object CreateDelegate(MethodInfo method) + { + return new Tuple(method.GetParameters().Select(x => x.ParameterType).ToArray(), Unsafe.As(CreateDelegateFromMethod(method))); + } + + internal static object CreateInvokerDelegate(MethodInfo method) + { + return Unsafe.As(CreateDelegateFromMethod(method, false)); + } + + internal static object MarshalAndInvoke(object delegateContext, IntPtr instancePtr, IntPtr paramPtr) + { + (Type[] types, InvokerDelegate deleg) = (Tuple)(delegateContext); + + IntPtr param1Ptr = Marshal.ReadIntPtr(paramPtr); + + T1 param1 = default; + if (param1Ptr != IntPtr.Zero) MarshalHelper.ToManaged(ref param1, param1Ptr, types[0].IsByRef); + + TRet ret = deleg(ref param1); + + // Marshal reference parameters back to original unmanaged references + if (types[0].IsByRef) + MarshalHelper.ToNative(ref param1, param1Ptr); + + return ret; + } + + internal static unsafe object InvokeThunk(object delegateContext, IntPtr instancePtr, IntPtr* paramPtrs) + { + ThunkInvokerDelegate deleg = Unsafe.As(delegateContext); + + T1 param1 = paramPtrs[0] != IntPtr.Zero ? (T1)GCHandle.FromIntPtr(paramPtrs[0]).Target : default(T1); + + TRet ret = deleg(param1); + + return ret; + } + } + + internal static class InvokerStaticRet2 + { + internal delegate TRet InvokerDelegate(ref T1 param1, ref T2 param2); + internal delegate TRet ThunkInvokerDelegate(T1 param1, T2 param2); + + internal static object CreateDelegate(MethodInfo method) + { + return new Tuple(method.GetParameters().Select(x => x.ParameterType).ToArray(), Unsafe.As(CreateDelegateFromMethod(method))); + } + + internal static object CreateInvokerDelegate(MethodInfo method) + { + return Unsafe.As(CreateDelegateFromMethod(method, false)); + } + + internal static object MarshalAndInvoke(object delegateContext, IntPtr instancePtr, IntPtr paramPtr) + { + (Type[] types, InvokerDelegate deleg) = (Tuple)(delegateContext); + + IntPtr param1Ptr = Marshal.ReadIntPtr(paramPtr); + IntPtr param2Ptr = Marshal.ReadIntPtr(paramPtr + IntPtr.Size); + + T1 param1 = default; + T2 param2 = default; + if (param1Ptr != IntPtr.Zero) MarshalHelper.ToManaged(ref param1, param1Ptr, types[0].IsByRef); + if (param2Ptr != IntPtr.Zero) MarshalHelper.ToManaged(ref param2, param2Ptr, types[1].IsByRef); + + TRet ret = deleg(ref param1, ref param2); + + // Marshal reference parameters back to original unmanaged references + if (types[0].IsByRef) + MarshalHelper.ToNative(ref param1, param1Ptr); + if (types[1].IsByRef) + MarshalHelper.ToNative(ref param2, param2Ptr); + + return ret; + } + + internal static unsafe object InvokeThunk(object delegateContext, IntPtr instancePtr, IntPtr* paramPtrs) + { + ThunkInvokerDelegate deleg = Unsafe.As(delegateContext); + + T1 param1 = paramPtrs[0] != IntPtr.Zero ? (T1)GCHandle.FromIntPtr(paramPtrs[0]).Target : default(T1); + T2 param2 = paramPtrs[1] != IntPtr.Zero ? (T2)GCHandle.FromIntPtr(paramPtrs[1]).Target : default(T2); + + TRet ret = deleg(param1, param2); + + return ret; + } + } + + internal static class InvokerStaticRet3 + { + internal delegate TRet InvokerDelegate(ref T1 param1, ref T2 param2, ref T3 param3); + internal delegate TRet ThunkInvokerDelegate(T1 param1, T2 param2, T3 param3); + + internal static object CreateDelegate(MethodInfo method) + { + return new Tuple(method.GetParameters().Select(x => x.ParameterType).ToArray(), Unsafe.As(CreateDelegateFromMethod(method))); + } + + internal static object CreateInvokerDelegate(MethodInfo method) + { + return Unsafe.As(CreateDelegateFromMethod(method, false)); + } + + internal static object MarshalAndInvoke(object delegateContext, IntPtr instancePtr, IntPtr paramPtr) + { + (Type[] types, InvokerDelegate deleg) = (Tuple)(delegateContext); + + IntPtr param1Ptr = Marshal.ReadIntPtr(paramPtr); + IntPtr param2Ptr = Marshal.ReadIntPtr(paramPtr + IntPtr.Size); + IntPtr param3Ptr = Marshal.ReadIntPtr(paramPtr + IntPtr.Size + IntPtr.Size); + + T1 param1 = default; + T2 param2 = default; + T3 param3 = default; + if (param1Ptr != IntPtr.Zero) MarshalHelper.ToManaged(ref param1, param1Ptr, types[0].IsByRef); + if (param2Ptr != IntPtr.Zero) MarshalHelper.ToManaged(ref param2, param2Ptr, types[1].IsByRef); + if (param3Ptr != IntPtr.Zero) MarshalHelper.ToManaged(ref param3, param3Ptr, types[2].IsByRef); + + TRet ret = deleg(ref param1, ref param2, ref param3); + + // Marshal reference parameters back to original unmanaged references + if (types[0].IsByRef) + MarshalHelper.ToNative(ref param1, param1Ptr); + if (types[1].IsByRef) + MarshalHelper.ToNative(ref param2, param2Ptr); + if (types[2].IsByRef) + MarshalHelper.ToNative(ref param3, param3Ptr); + + return ret; + } + + internal static unsafe object InvokeThunk(object delegateContext, IntPtr instancePtr, IntPtr* paramPtrs) + { + ThunkInvokerDelegate deleg = Unsafe.As(delegateContext); + + T1 param1 = paramPtrs[0] != IntPtr.Zero ? (T1)GCHandle.FromIntPtr(paramPtrs[0]).Target : default(T1); + T2 param2 = paramPtrs[1] != IntPtr.Zero ? (T2)GCHandle.FromIntPtr(paramPtrs[1]).Target : default(T2); + T3 param3 = paramPtrs[2] != IntPtr.Zero ? (T3)GCHandle.FromIntPtr(paramPtrs[2]).Target : default(T3); + + TRet ret = deleg(param1, param2, param3); + + return ret; + } + } + + internal static class InvokerStaticRet4 + { + internal delegate TRet InvokerDelegate(ref T1 param1, ref T2 param2, ref T3 param3, ref T4 param4); + internal delegate TRet ThunkInvokerDelegate(T1 param1, T2 param2, T3 param3, T4 param4); + + internal static object CreateDelegate(MethodInfo method) + { + return new Tuple(method.GetParameters().Select(x => x.ParameterType).ToArray(), Unsafe.As(CreateDelegateFromMethod(method))); + } + + internal static object CreateInvokerDelegate(MethodInfo method) + { + return Unsafe.As(CreateDelegateFromMethod(method, false)); + } + + internal static object MarshalAndInvoke(object delegateContext, IntPtr instancePtr, IntPtr paramPtr) + { + (Type[] types, InvokerDelegate deleg) = (Tuple)(delegateContext); + + IntPtr param1Ptr = Marshal.ReadIntPtr(paramPtr); + IntPtr param2Ptr = Marshal.ReadIntPtr(paramPtr + IntPtr.Size); + IntPtr param3Ptr = Marshal.ReadIntPtr(paramPtr + IntPtr.Size + IntPtr.Size); + IntPtr param4Ptr = Marshal.ReadIntPtr(paramPtr + IntPtr.Size + IntPtr.Size + IntPtr.Size); + + T1 param1 = default; + T2 param2 = default; + T3 param3 = default; + T4 param4 = default; + if (param1Ptr != IntPtr.Zero) MarshalHelper.ToManaged(ref param1, param1Ptr, types[0].IsByRef); + if (param2Ptr != IntPtr.Zero) MarshalHelper.ToManaged(ref param2, param2Ptr, types[1].IsByRef); + if (param3Ptr != IntPtr.Zero) MarshalHelper.ToManaged(ref param3, param3Ptr, types[2].IsByRef); + if (param4Ptr != IntPtr.Zero) MarshalHelper.ToManaged(ref param4, param4Ptr, types[3].IsByRef); + + TRet ret = deleg(ref param1, ref param2, ref param3, ref param4); + + // Marshal reference parameters back to original unmanaged references + if (types[0].IsByRef) + MarshalHelper.ToNative(ref param1, param1Ptr); + if (types[1].IsByRef) + MarshalHelper.ToNative(ref param2, param2Ptr); + if (types[2].IsByRef) + MarshalHelper.ToNative(ref param3, param3Ptr); + if (types[3].IsByRef) + MarshalHelper.ToNative(ref param4, param4Ptr); + + return ret; + } + + internal static unsafe object InvokeThunk(object delegateContext, IntPtr instancePtr, IntPtr* paramPtrs) + { + ThunkInvokerDelegate deleg = Unsafe.As(delegateContext); + + T1 param1 = paramPtrs[0] != IntPtr.Zero ? (T1)GCHandle.FromIntPtr(paramPtrs[0]).Target : default(T1); + T2 param2 = paramPtrs[1] != IntPtr.Zero ? (T2)GCHandle.FromIntPtr(paramPtrs[1]).Target : default(T2); + T3 param3 = paramPtrs[2] != IntPtr.Zero ? (T3)GCHandle.FromIntPtr(paramPtrs[2]).Target : default(T3); + T4 param4 = paramPtrs[3] != IntPtr.Zero ? (T4)GCHandle.FromIntPtr(paramPtrs[3]).Target : default(T4); + + TRet ret = deleg(param1, param2, param3, param4); + + return ret; + } + } + } + } +} + +#endif diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs index 88113a756..71c5d7eaa 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs @@ -1449,30 +1449,29 @@ namespace Flax.Build.Bindings } else if (fieldInfo.Type.Type == "Array") { + string originalElementType = originalType.Substring(0, originalType.Length - 2); if (internalType) { // Marshal blittable array elements back to original non-blittable elements - string originalElementType = originalType.Substring(0, originalType.Length - 2); string originalElementTypeMarshaller = originalElementType + "Marshaller"; string internalElementType = $"{originalElementTypeMarshaller}.{originalElementType}Internal"; - toManagedContent.Append($"managed.{fieldInfo.Name} != IntPtr.Zero ? NativeInterop.NativeArrayToManagedArray<{originalElementType}, {internalElementType}>(((ManagedArray)GCHandle.FromIntPtr(managed.{fieldInfo.Name}).Target).array as {internalElementType}[], {originalElementTypeMarshaller}.ToManaged) : null"); + toManagedContent.Append($"managed.{fieldInfo.Name} != IntPtr.Zero ? NativeInterop.NativeArrayToManagedArray<{originalElementType}, {internalElementType}>(((ManagedArray)GCHandle.FromIntPtr(managed.{fieldInfo.Name}).Target).GetSpan<{internalElementType}>(), {originalElementTypeMarshaller}.ToManaged) : null"); toNativeContent.Append($"GCHandle.ToIntPtr(GCHandle.Alloc(ManagedArray.Get(managed.{fieldInfo.Name}), GCHandleType.Weak))"); - freeContents.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ GCHandle handle = GCHandle.FromIntPtr(unmanaged.{fieldInfo.Name}); {internalElementType}[] values = ({internalElementType}[])(((ManagedArray)handle.Target).array); foreach (var value in values) {{ {originalElementTypeMarshaller}.Free(value); }} ((ManagedArray)handle.Target).Release(); handle.Free(); }}"); - freeContents2.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ GCHandle handle = GCHandle.FromIntPtr(unmanaged.{fieldInfo.Name}); {internalElementType}[] values = ({internalElementType}[])(((ManagedArray)handle.Target).array); foreach (var value in values) {{ {originalElementTypeMarshaller}.Free(value); }} ((ManagedArray)handle.Target).Release(); handle.Free(); }}"); + freeContents.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ GCHandle handle = GCHandle.FromIntPtr(unmanaged.{fieldInfo.Name}); Span<{internalElementType}> values = ((ManagedArray)handle.Target).GetSpan<{internalElementType}>(); foreach (var value in values) {{ {originalElementTypeMarshaller}.Free(value); }} ((ManagedArray)handle.Target).Release(); handle.Free(); }}"); + freeContents2.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ GCHandle handle = GCHandle.FromIntPtr(unmanaged.{fieldInfo.Name}); Span<{internalElementType}> values = ((ManagedArray)handle.Target).GetSpan<{internalElementType}>(); foreach (var value in values) {{ {originalElementTypeMarshaller}.Free(value); }} ((ManagedArray)handle.Target).Release(); handle.Free(); }}"); } else if (fieldInfo.Type.GenericArgs[0].IsObjectRef) { // Array elements passed as GCHandles - string originalElementType = originalType.Substring(0, originalType.Length - 2); toManagedContent.Append($"managed.{fieldInfo.Name} != IntPtr.Zero ? NativeInterop.GCHandleArrayToManagedArray<{originalElementType}>((ManagedArray)GCHandle.FromIntPtr(managed.{fieldInfo.Name}).Target) : null"); toNativeContent.Append($"managed.{fieldInfo.Name}?.Length > 0 ? GCHandle.ToIntPtr(GCHandle.Alloc(ManagedArray.Get(NativeInterop.ManagedArrayToGCHandleArray(managed.{fieldInfo.Name})), GCHandleType.Weak)) : IntPtr.Zero"); - freeContents.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ GCHandle handle = GCHandle.FromIntPtr(unmanaged.{fieldInfo.Name}); IntPtr[] ptrs = (IntPtr[])(((ManagedArray)handle.Target).array); foreach (var ptr in ptrs) {{ if (ptr != IntPtr.Zero) {{ GCHandle.FromIntPtr(ptr).Free(); }} }} ((ManagedArray)handle.Target).Release(); handle.Free(); }}"); - freeContents2.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ GCHandle handle = GCHandle.FromIntPtr(unmanaged.{fieldInfo.Name}); IntPtr[] ptrs = (IntPtr[])(((ManagedArray)handle.Target).array); foreach (var ptr in ptrs) {{ if (ptr != IntPtr.Zero) {{ GCHandle.FromIntPtr(ptr).Free(); }} }} ((ManagedArray)handle.Target).Release(); handle.Free(); }}"); + freeContents.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ GCHandle handle = GCHandle.FromIntPtr(unmanaged.{fieldInfo.Name}); Span ptrs = ((ManagedArray)handle.Target).GetSpan(); foreach (var ptr in ptrs) {{ if (ptr != IntPtr.Zero) {{ GCHandle.FromIntPtr(ptr).Free(); }} }} ((ManagedArray)handle.Target).Release(); handle.Free(); }}"); + freeContents2.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ GCHandle handle = GCHandle.FromIntPtr(unmanaged.{fieldInfo.Name}); Span ptrs = ((ManagedArray)handle.Target).GetSpan(); foreach (var ptr in ptrs) {{ if (ptr != IntPtr.Zero) {{ GCHandle.FromIntPtr(ptr).Free(); }} }} ((ManagedArray)handle.Target).Release(); handle.Free(); }}"); } else { // Blittable array elements - toManagedContent.Append($"managed.{fieldInfo.Name} != IntPtr.Zero ? ({originalType})(((ManagedArray)GCHandle.FromIntPtr(managed.{fieldInfo.Name}).Target).array) : null"); + toManagedContent.Append($"managed.{fieldInfo.Name} != IntPtr.Zero ? ((ManagedArray)GCHandle.FromIntPtr(managed.{fieldInfo.Name}).Target).GetArray<{originalElementType}>() : null"); toNativeContent.Append($"managed.{fieldInfo.Name}?.Length > 0 ? GCHandle.ToIntPtr(GCHandle.Alloc(ManagedArray.Get(managed.{fieldInfo.Name}), GCHandleType.Weak)) : IntPtr.Zero"); freeContents.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ GCHandle handle = GCHandle.FromIntPtr(unmanaged.{fieldInfo.Name}); ((ManagedArray)handle.Target).Release(); handle.Free(); }}"); freeContents2.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ GCHandle handle = GCHandle.FromIntPtr(unmanaged.{fieldInfo.Name}); ((ManagedArray)handle.Target).Release(); handle.Free(); }}"); @@ -1545,13 +1544,13 @@ namespace Flax.Build.Bindings // NOTE: GCHandles of FlaxEngine.Object must not be released unless they were allocated by this marshaller contents.Append(indent).AppendLine($"public struct Bidirectional").Append(indent).AppendLine("{"); contents.Append(indent2).AppendLine($"{structureInfo.Name} managed;"); - contents.Append(indent2).AppendLine($"{structureInfo.Name}Internal? unmanaged;"); - contents.Append(indent2).AppendLine($"public void FromManaged({structureInfo.Name} managed) {{ this.managed = managed; }}"); - contents.Append(indent2).AppendLine($"public {structureInfo.Name}Internal ToUnmanaged() {{ unmanaged = {marshallerName}.ToNative(managed); return unmanaged.Value; }}"); + contents.Append(indent2).AppendLine($"{structureInfo.Name}Internal unmanaged;"); + contents.Append(indent2).AppendLine($"public void FromManaged({structureInfo.Name} managed) => this.managed = managed;"); + contents.Append(indent2).AppendLine($"public {structureInfo.Name}Internal ToUnmanaged() {{ unmanaged = {marshallerName}.ToNative(managed); return unmanaged; }}"); //contents.Append(indent2).AppendLine($"public void FromUnmanaged({structureInfo.Name}Internal unmanaged) {{ {marshallerName}.Free(this.unmanaged.Value); this.unmanaged = unmanaged; }}"); - contents.Append(indent2).AppendLine($"public void FromUnmanaged({structureInfo.Name}Internal unmanaged) {{ this.unmanaged = unmanaged; }}"); - contents.Append(indent2).AppendLine($"public {structureInfo.Name} ToManaged() {{ managed = {marshallerName}.ToManaged(unmanaged.Value); return managed; }}"); - contents.Append(indent2).AppendLine($"public void Free() {{ if (unmanaged.HasValue) {{ NativeToManaged.Free(unmanaged.Value); unmanaged = null; }} }}"); + contents.Append(indent2).AppendLine($"public void FromUnmanaged({structureInfo.Name}Internal unmanaged) => this.unmanaged = unmanaged;"); + contents.Append(indent2).AppendLine($"public {structureInfo.Name} ToManaged() {{ managed = {marshallerName}.ToManaged(unmanaged); return managed; }}"); + contents.Append(indent2).AppendLine($"public void Free() => NativeToManaged.Free(unmanaged);"); contents.Append(indent).AppendLine("}"); // Bidirectional stateless shape