From 34988065887fdc5ac1fdf48ae083acbb8a1ddd62 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sat, 19 Aug 2023 17:45:42 +0200 Subject: [PATCH] Add support for accessing scripting properties via `ManagedBinaryModule` fields API --- Source/Engine/Scripting/BinaryModule.cpp | 110 +++++++++++++++++++---- 1 file changed, 93 insertions(+), 17 deletions(-) diff --git a/Source/Engine/Scripting/BinaryModule.cpp b/Source/Engine/Scripting/BinaryModule.cpp index 6ffdb14a9..820c919f3 100644 --- a/Source/Engine/Scripting/BinaryModule.cpp +++ b/Source/Engine/Scripting/BinaryModule.cpp @@ -9,6 +9,7 @@ #include "ManagedCLR/MClass.h" #include "ManagedCLR/MMethod.h" #include "ManagedCLR/MField.h" +#include "ManagedCLR/MProperty.h" #include "ManagedCLR/MUtils.h" #include "ManagedCLR/MException.h" #include "FlaxEngine.Gen.h" @@ -1324,47 +1325,97 @@ void ManagedBinaryModule::GetMethodSignature(void* method, ScriptingTypeMethodSi #endif } +// Pointers with the highest bit turned on are properties +#if PLATFORM_64BITS +#define ManagedBinaryModuleFieldIsPropertyBit (uintptr)(1ull << 63) +#else +#define ManagedBinaryModuleFieldIsPropertyBit (uintptr)(1ul << 31) +#endif + void* ManagedBinaryModule::FindField(const ScriptingTypeHandle& typeHandle, const StringAnsiView& name) { const ScriptingType& type = typeHandle.GetType(); - return type.ManagedClass ? type.ManagedClass->GetField(name.Get()) : nullptr; + void* result = type.ManagedClass ? type.ManagedClass->GetField(name.Get()) : nullptr; + if (!result && type.ManagedClass) + { + result = type.ManagedClass->GetProperty(name.Get()); + if (result) + result = (void*)((uintptr)result | ManagedBinaryModuleFieldIsPropertyBit); + } + return result; } void ManagedBinaryModule::GetFieldSignature(void* field, ScriptingTypeFieldSignature& fieldSignature) { #if USE_CSHARP - const auto mField = (MField*)field; - fieldSignature.Name = mField->GetName(); - fieldSignature.ValueType = MoveTemp(MUtils::UnboxVariantType(mField->GetType())); - fieldSignature.IsStatic = mField->IsStatic(); + if ((uintptr)field & ManagedBinaryModuleFieldIsPropertyBit) + { + const auto mProperty = (MProperty*)((uintptr)field & ~ManagedBinaryModuleFieldIsPropertyBit); + fieldSignature.Name = mProperty->GetName(); + fieldSignature.ValueType = MoveTemp(MUtils::UnboxVariantType(mProperty->GetType())); + fieldSignature.IsStatic = mProperty->IsStatic(); + } + else + { + const auto mField = (MField*)field; + fieldSignature.Name = mField->GetName(); + fieldSignature.ValueType = MoveTemp(MUtils::UnboxVariantType(mField->GetType())); + fieldSignature.IsStatic = mField->IsStatic(); + } #endif } bool ManagedBinaryModule::GetFieldValue(void* field, const Variant& instance, Variant& result) { #if USE_CSHARP - const auto mField = (MField*)field; + bool isStatic; + MClass* parentClass; + StringAnsiView name; + if ((uintptr)field & ManagedBinaryModuleFieldIsPropertyBit) + { + const auto mProperty = (MProperty*)((uintptr)field & ~ManagedBinaryModuleFieldIsPropertyBit); + isStatic = mProperty->IsStatic(); + parentClass = mProperty->GetParentClass(); + name = mProperty->GetName(); + } + else + { + const auto mField = (MField*)field; + isStatic = mField->IsStatic(); + parentClass = mField->GetParentClass(); + name = mField->GetName(); + } // Get instance object MObject* instanceObject = nullptr; - if (!mField->IsStatic()) + if (!isStatic) { // Box instance into C# object instanceObject = MUtils::BoxVariant(instance); // Validate instance - if (!instanceObject || !MCore::Object::GetClass(instanceObject)->IsSubClassOf(mField->GetParentClass())) + if (!instanceObject || !MCore::Object::GetClass(instanceObject)->IsSubClassOf(parentClass)) { if (!instanceObject) - LOG(Error, "Failed to get field '{0}.{1}' without object instance", String(mField->GetParentClass()->GetFullName()), String(mField->GetName())); + LOG(Error, "Failed to get '{0}.{1}' without object instance", String(parentClass->GetFullName()), String(name)); else - LOG(Error, "Failed to get field '{0}.{1}' with invalid object instance of type '{2}'", String(mField->GetParentClass()->GetFullName()), String(mField->GetName()), String(MUtils::GetClassFullname(instanceObject))); + LOG(Error, "Failed to get '{0}.{1}' with invalid object instance of type '{2}'", String(parentClass->GetFullName()), String(name), String(MUtils::GetClassFullname(instanceObject))); return true; } } // Get the value - MObject* resultObject = mField->GetValueBoxed(instanceObject); + MObject* resultObject; + if ((uintptr)field & ManagedBinaryModuleFieldIsPropertyBit) + { + const auto mProperty = (MProperty*)((uintptr)field & ~ManagedBinaryModuleFieldIsPropertyBit); + resultObject = mProperty->GetValue(instanceObject, nullptr); + } + else + { + const auto mField = (MField*)field; + resultObject = mField->GetValueBoxed(instanceObject); + } result = MUtils::UnboxVariant(resultObject); return false; #else @@ -1375,29 +1426,54 @@ bool ManagedBinaryModule::GetFieldValue(void* field, const Variant& instance, Va bool ManagedBinaryModule::SetFieldValue(void* field, const Variant& instance, Variant& value) { #if USE_CSHARP - const auto mField = (MField*)field; + bool isStatic; + MClass* parentClass; + StringAnsiView name; + if ((uintptr)field & ManagedBinaryModuleFieldIsPropertyBit) + { + const auto mProperty = (MProperty*)((uintptr)field & ~ManagedBinaryModuleFieldIsPropertyBit); + isStatic = mProperty->IsStatic(); + parentClass = mProperty->GetParentClass(); + name = mProperty->GetName(); + } + else + { + const auto mField = (MField*)field; + isStatic = mField->IsStatic(); + parentClass = mField->GetParentClass(); + name = mField->GetName(); + } // Get instance object MObject* instanceObject = nullptr; - if (!mField->IsStatic()) + if (!isStatic) { // Box instance into C# object instanceObject = MUtils::BoxVariant(instance); // Validate instance - if (!instanceObject || !MCore::Object::GetClass(instanceObject)->IsSubClassOf(mField->GetParentClass())) + if (!instanceObject || !MCore::Object::GetClass(instanceObject)->IsSubClassOf(parentClass)) { if (!instanceObject) - LOG(Error, "Failed to set field '{0}.{1}' without object instance", String(mField->GetParentClass()->GetFullName()), String(mField->GetName())); + LOG(Error, "Failed to set '{0}.{1}' without object instance", String(parentClass->GetFullName()), String(name)); else - LOG(Error, "Failed to set field '{0}.{1}' with invalid object instance of type '{2}'", String(mField->GetParentClass()->GetFullName()), String(mField->GetName()), String(MUtils::GetClassFullname(instanceObject))); + LOG(Error, "Failed to set '{0}.{1}' with invalid object instance of type '{2}'", String(parentClass->GetFullName()), String(name), String(MUtils::GetClassFullname(instanceObject))); return true; } } // Set the value bool failed = false; - mField->SetValue(instanceObject, MUtils::VariantToManagedArgPtr(value, mField->GetType(), failed)); + if ((uintptr)field & ManagedBinaryModuleFieldIsPropertyBit) + { + const auto mProperty = (MProperty*)((uintptr)field & ~ManagedBinaryModuleFieldIsPropertyBit); + mProperty->SetValue(instanceObject, MUtils::BoxVariant(value), nullptr); + } + else + { + const auto mField = (MField*)field; + mField->SetValue(instanceObject, MUtils::VariantToManagedArgPtr(value, mField->GetType(), failed)); + } return failed; #else return true;