From e860f969be581f2dabb019863139a9aecd37cd2c Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 4 Oct 2024 15:57:41 +0200 Subject: [PATCH] Add attributes support for dotnet interop for methods, fields and properties --- .../Engine/Engine/NativeInterop.Unmanaged.cs | 54 ++++++++- Source/Engine/Engine/NativeInterop.cs | 2 + .../Engine/Scripting/ManagedCLR/MProperty.h | 4 +- Source/Engine/Scripting/Runtime/DotNet.cpp | 104 +++++++++--------- 4 files changed, 107 insertions(+), 57 deletions(-) diff --git a/Source/Engine/Engine/NativeInterop.Unmanaged.cs b/Source/Engine/Engine/NativeInterop.Unmanaged.cs index 852dcb52c..e3d66a2ef 100644 --- a/Source/Engine/Engine/NativeInterop.Unmanaged.cs +++ b/Source/Engine/Engine/NativeInterop.Unmanaged.cs @@ -49,6 +49,7 @@ namespace FlaxEngine.Interop internal struct NativePropertyDefinitions { internal IntPtr name; + internal ManagedHandle propertyHandle; internal ManagedHandle getterHandle; internal ManagedHandle setterHandle; internal uint getterAttributes; @@ -379,6 +380,17 @@ namespace FlaxEngine.Interop for (int i = 0; i < properties.Length; i++) { var property = properties[i]; + + ManagedHandle propertyHandle = ManagedHandle.Alloc(property); +#if FLAX_EDITOR + if (type.IsCollectible) + propertyHandleCacheCollectible.Add(propertyHandle); + else +#endif + { + propertyHandleCache.Add(propertyHandle); + } + IntPtr ptr = IntPtr.Add(new IntPtr(arr), Unsafe.SizeOf() * i); var getterMethod = property.GetGetMethod(true); @@ -387,6 +399,7 @@ namespace FlaxEngine.Interop var classProperty = new NativePropertyDefinitions { name = NativeAllocStringAnsi(property.Name), + propertyHandle = propertyHandle, }; if (getterMethod != null) { @@ -404,12 +417,8 @@ namespace FlaxEngine.Interop *classPropertiesCount = properties.Length; } - [UnmanagedCallersOnly] - internal static void GetClassAttributes(ManagedHandle typeHandle, ManagedHandle** classAttributes, int* classAttributesCount) + internal static void GetAttributes(object[] attributeValues, ManagedHandle** classAttributes, int* classAttributesCount) { - Type type = Unsafe.As(typeHandle.Target); - object[] attributeValues = type.GetCustomAttributes(false); - ManagedHandle* arr = (ManagedHandle*)NativeAlloc(attributeValues.Length, Unsafe.SizeOf()); for (int i = 0; i < attributeValues.Length; i++) { @@ -424,6 +433,38 @@ namespace FlaxEngine.Interop *classAttributesCount = attributeValues.Length; } + [UnmanagedCallersOnly] + internal static void GetClassAttributes(ManagedHandle typeHandle, ManagedHandle** classAttributes, int* classAttributesCount) + { + Type type = Unsafe.As(typeHandle.Target); + object[] attributeValues = type.GetCustomAttributes(false); + GetAttributes(attributeValues, classAttributes, classAttributesCount); + } + + [UnmanagedCallersOnly] + internal static void GetMethodAttributes(ManagedHandle methodHandle, ManagedHandle** classAttributes, int* classAttributesCount) + { + MethodHolder methodHolder = Unsafe.As(methodHandle.Target); + object[] attributeValues = methodHolder.method.GetCustomAttributes(false); + GetAttributes(attributeValues, classAttributes, classAttributesCount); + } + + [UnmanagedCallersOnly] + internal static void GetFieldAttributes(ManagedHandle fieldHandle, ManagedHandle** classAttributes, int* classAttributesCount) + { + FieldHolder field = Unsafe.As(fieldHandle.Target); + object[] attributeValues = field.field.GetCustomAttributes(false); + GetAttributes(attributeValues, classAttributes, classAttributesCount); + } + + [UnmanagedCallersOnly] + internal static void GetPropertyAttributes(ManagedHandle propertyHandle, ManagedHandle** classAttributes, int* classAttributesCount) + { + PropertyInfo property = Unsafe.As(propertyHandle.Target); + object[] attributeValues = property.GetCustomAttributes(false); + GetAttributes(attributeValues, classAttributes, classAttributesCount); + } + [UnmanagedCallersOnly] internal static ManagedHandle GetCustomAttribute(ManagedHandle typeHandle, ManagedHandle attributeHandle) { @@ -1027,6 +1068,9 @@ namespace FlaxEngine.Interop foreach (var handle in fieldHandleCacheCollectible) handle.Free(); fieldHandleCacheCollectible.Clear(); + foreach (var handle in propertyHandleCacheCollectible) + handle.Free(); + propertyHandleCacheCollectible.Clear(); _typeSizeCache.Clear(); diff --git a/Source/Engine/Engine/NativeInterop.cs b/Source/Engine/Engine/NativeInterop.cs index 8317383cd..3591df299 100644 --- a/Source/Engine/Engine/NativeInterop.cs +++ b/Source/Engine/Engine/NativeInterop.cs @@ -38,11 +38,13 @@ namespace FlaxEngine.Interop private static ConcurrentDictionary cachedDelegates = new(); private static Dictionary managedTypes = new(new TypeComparer()); private static List fieldHandleCache = new(); + private static List propertyHandleCache = new(); #if FLAX_EDITOR private static List methodHandlesCollectible = new(); private static ConcurrentDictionary cachedDelegatesCollectible = new(); private static Dictionary managedTypesCollectible = new(new TypeComparer()); private static List fieldHandleCacheCollectible = new(); + private static List propertyHandleCacheCollectible = new(); #endif private static Dictionary classAttributesCacheCollectible = new(); private static Dictionary assemblyHandles = new(); diff --git a/Source/Engine/Scripting/ManagedCLR/MProperty.h b/Source/Engine/Scripting/ManagedCLR/MProperty.h index a9ce918f8..dbbed8ab3 100644 --- a/Source/Engine/Scripting/ManagedCLR/MProperty.h +++ b/Source/Engine/Scripting/ManagedCLR/MProperty.h @@ -16,6 +16,8 @@ class FLAXENGINE_API MProperty protected: #if USE_MONO MonoProperty* _monoProperty; +#elif USE_NETCORE + void* _handle; #endif mutable MMethod* _getMethod; @@ -34,7 +36,7 @@ public: #if USE_MONO explicit MProperty(MonoProperty* monoProperty, const char* name, MClass* parentClass); #elif USE_NETCORE - MProperty(MClass* parentClass, const char* name, void* getterHandle, void* setterHandle, MMethodAttributes getterAttributes, MMethodAttributes setterAttributes); + MProperty(MClass* parentClass, const char* name, void* handle, void* getterHandle, void* setterHandle, MMethodAttributes getterAttributes, MMethodAttributes setterAttributes); #endif /// diff --git a/Source/Engine/Scripting/Runtime/DotNet.cpp b/Source/Engine/Scripting/Runtime/DotNet.cpp index c2155a148..4a71e9c2f 100644 --- a/Source/Engine/Scripting/Runtime/DotNet.cpp +++ b/Source/Engine/Scripting/Runtime/DotNet.cpp @@ -211,7 +211,25 @@ MClass* GetClass(MType* typeHandle); MClass* GetOrCreateClass(MType* typeHandle); MType* GetObjectType(MObject* obj); -void* GetCustomAttribute(const MClass* klass, const MClass* attributeClass); +void* GetCustomAttribute(const Array& attributes, const MClass* attributeClass) +{ + for (MObject* attr : attributes) + { + MClass* attrClass = MCore::Object::GetClass(attr); + if (attrClass == attributeClass) + return attr; + } + return nullptr; +} + +void GetCustomAttributes(Array& result, void* handle, void* getAttributesFunc) +{ + MObject** attributes; + int numAttributes; + CallStaticMethod(getAttributesFunc, handle, &attributes, &numAttributes); + result.Set(attributes, numAttributes); + MCore::GC::FreeMemory(attributes); +} // Structures used to pass information from runtime, must match with the structures in managed side struct NativeClassDefinitions @@ -244,6 +262,7 @@ struct NativeFieldDefinitions struct NativePropertyDefinitions { const char* name; + void* propertyHandle; void* getterHandle; void* setterHandle; MMethodAttributes getterAttributes; @@ -1089,7 +1108,7 @@ const Array& MClass::GetProperties() const for (int i = 0; i < numProperties; i++) { const NativePropertyDefinitions& definition = foundProperties[i]; - MProperty* property = New(const_cast(this), definition.name, definition.getterHandle, definition.setterHandle, definition.getterAttributes, definition.setterAttributes); + MProperty* property = New(const_cast(this), definition.name, definition.propertyHandle, definition.getterHandle, definition.setterHandle, definition.getterAttributes, definition.setterAttributes); _properties[i] = property; MCore::GC::FreeMemory((void*)definition.name); } @@ -1125,7 +1144,7 @@ const Array& MClass::GetInterfaces() const bool MClass::HasAttribute(const MClass* klass) const { - return GetCustomAttribute(this, klass) != nullptr; + return GetCustomAttribute(GetAttributes(), klass) != nullptr; } bool MClass::HasAttribute() const @@ -1135,7 +1154,7 @@ bool MClass::HasAttribute() const MObject* MClass::GetAttribute(const MClass* klass) const { - return (MObject*)GetCustomAttribute(this, klass); + return (MObject*)GetCustomAttribute(GetAttributes(), klass); } const Array& MClass::GetAttributes() const @@ -1145,14 +1164,8 @@ const Array& MClass::GetAttributes() const ScopeLock lock(BinaryModule::Locker); if (_hasCachedAttributes) return _attributes; - - MObject** attributes; - int numAttributes; static void* GetClassAttributesPtr = GetStaticMethodPointer(TEXT("GetClassAttributes")); - CallStaticMethod(GetClassAttributesPtr, _handle, &attributes, &numAttributes); - _attributes.Set(attributes, numAttributes); - MCore::GC::FreeMemory(attributes); - + GetCustomAttributes(_attributes, _handle, GetClassAttributesPtr); _hasCachedAttributes = true; return _attributes; } @@ -1191,17 +1204,17 @@ MMethod* MEvent::GetRemoveMethod() const bool MEvent::HasAttribute(const MClass* klass) const { - return false; // TODO: implement MEvent in .NET + return GetCustomAttribute(GetAttributes(), klass) != nullptr; } bool MEvent::HasAttribute() const { - return false; // TODO: implement MEvent in .NET + return !GetAttributes().IsEmpty(); } MObject* MEvent::GetAttribute(const MClass* klass) const { - return nullptr; // TODO: implement MEvent in .NET + return (MObject*)GetCustomAttribute(GetAttributes(), klass); } const Array& MEvent::GetAttributes() const @@ -1313,29 +1326,29 @@ void MField::SetValue(MObject* instance, void* value) const bool MField::HasAttribute(const MClass* klass) const { - // TODO: implement MField attributes in .NET - return false; + return GetCustomAttribute(GetAttributes(), klass) != nullptr; } bool MField::HasAttribute() const { - // TODO: implement MField attributes in .NET - return false; + return !GetAttributes().IsEmpty(); } MObject* MField::GetAttribute(const MClass* klass) const { - // TODO: implement MField attributes in .NET - return nullptr; + return (MObject*)GetCustomAttribute(GetAttributes(), klass); } const Array& MField::GetAttributes() const { if (_hasCachedAttributes) return _attributes; + ScopeLock lock(BinaryModule::Locker); + if (_hasCachedAttributes) + return _attributes; + static void* GetFieldAttributesPtr = GetStaticMethodPointer(TEXT("GetFieldAttributes")); + GetCustomAttributes(_attributes, _handle, GetFieldAttributesPtr); _hasCachedAttributes = true; - - // TODO: implement MField attributes in .NET return _attributes; } @@ -1475,35 +1488,36 @@ bool MMethod::GetParameterIsOut(int32 paramIdx) const bool MMethod::HasAttribute(const MClass* klass) const { - // TODO: implement MMethod attributes in .NET - return false; + return GetCustomAttribute(GetAttributes(), klass) != nullptr; } bool MMethod::HasAttribute() const { - // TODO: implement MMethod attributes in .NET - return false; + return !GetAttributes().IsEmpty(); } MObject* MMethod::GetAttribute(const MClass* klass) const { - // TODO: implement MMethod attributes in .NET - return nullptr; + return (MObject*)GetCustomAttribute(GetAttributes(), klass); } const Array& MMethod::GetAttributes() const { if (_hasCachedAttributes) return _attributes; + ScopeLock lock(BinaryModule::Locker); + if (_hasCachedAttributes) + return _attributes; + static void* GetMethodAttributesPtr = GetStaticMethodPointer(TEXT("GetMethodAttributes")); + GetCustomAttributes(_attributes, _handle, GetMethodAttributesPtr); _hasCachedAttributes = true; - - // TODO: implement MMethod attributes in .NET return _attributes; } -MProperty::MProperty(MClass* parentClass, const char* name, void* getterHandle, void* setterHandle, MMethodAttributes getterAttributes, MMethodAttributes setterAttributes) +MProperty::MProperty(MClass* parentClass, const char* name, void* handle, void* getterHandle, void* setterHandle, MMethodAttributes getterAttributes, MMethodAttributes setterAttributes) : _parentClass(parentClass) , _name(name) + , _handle(handle) , _hasCachedAttributes(false) { _hasGetMethod = getterHandle != nullptr; @@ -1552,29 +1566,29 @@ void MProperty::SetValue(MObject* instance, void* value, MObject** exception) co bool MProperty::HasAttribute(const MClass* klass) const { - // TODO: implement MProperty attributes in .NET - return false; + return GetCustomAttribute(GetAttributes(), klass) != nullptr; } bool MProperty::HasAttribute() const { - // TODO: implement MProperty attributes in .NET - return false; + return !GetAttributes().IsEmpty(); } MObject* MProperty::GetAttribute(const MClass* klass) const { - // TODO: implement MProperty attributes in .NET - return nullptr; + return (MObject*)GetCustomAttribute(GetAttributes(), klass); } const Array& MProperty::GetAttributes() const { if (_hasCachedAttributes) return _attributes; + ScopeLock lock(BinaryModule::Locker); + if (_hasCachedAttributes) + return _attributes; + static void* GetPropertyAttributesPtr = GetStaticMethodPointer(TEXT("GetPropertyAttributes")); + GetCustomAttributes(_attributes, _handle, GetPropertyAttributesPtr); _hasCachedAttributes = true; - - // TODO: implement MProperty attributes in .NET return _attributes; } @@ -1637,18 +1651,6 @@ MType* GetObjectType(MObject* obj) return (MType*)typeHandle; } -void* GetCustomAttribute(const MClass* klass, const MClass* attributeClass) -{ - const Array& attributes = klass->GetAttributes(); - for (MObject* attr : attributes) - { - MClass* attrClass = MCore::Object::GetClass(attr); - if (attrClass == attributeClass) - return attr; - } - return nullptr; -} - #if DOTNET_HOST_CORECLR const char_t* NativeInteropTypeName = FLAX_CORECLR_TEXT("FlaxEngine.Interop.NativeInterop, FlaxEngine.CSharp");