From 0694f87b0d2b858d4296b3932410f9144624e71a Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 28 Mar 2023 12:01:55 +0200 Subject: [PATCH] Fixes for Visual Scripting interop via C# on new dotnet7 hosting --- Source/Engine/Engine/NativeInterop.cs | 43 ++++++---- Source/Engine/Engine/NativeInterop_Invoker.cs | 80 +++++++++---------- Source/Engine/Scripting/BinaryModule.cpp | 11 +-- Source/Engine/Scripting/ManagedCLR/MCore.h | 1 + Source/Engine/Scripting/ManagedCLR/MUtils.cpp | 9 ++- Source/Engine/Scripting/Runtime/DotNet.cpp | 27 +++++-- Source/Engine/Scripting/Runtime/Mono.cpp | 6 ++ 7 files changed, 106 insertions(+), 71 deletions(-) diff --git a/Source/Engine/Engine/NativeInterop.cs b/Source/Engine/Engine/NativeInterop.cs index 5bc40a03f..1ab25c3e9 100644 --- a/Source/Engine/Engine/NativeInterop.cs +++ b/Source/Engine/Engine/NativeInterop.cs @@ -2857,6 +2857,29 @@ namespace FlaxEngine valueHandle.Free(); } + [UnmanagedCallersOnly] + internal static ManagedHandle GetTypeClass(ManagedHandle typeHandle) + { + Type type = Unsafe.As(typeHandle.Target); + if (type.IsByRef) + type = type.GetElementType(); // Drop reference type (&) to get actual value type + return GetTypeGCHandle(type); + } + + [UnmanagedCallersOnly] + internal static bool GetTypeIsPointer(ManagedHandle typeHandle) + { + Type type = Unsafe.As(typeHandle.Target); + return type.IsPointer; + } + + [UnmanagedCallersOnly] + internal static bool GetTypeIsReference(ManagedHandle typeHandle) + { + Type type = Unsafe.As(typeHandle.Target); + return type.IsByRef; + } + internal enum MTypes : uint { End = 0x00, @@ -2901,6 +2924,8 @@ namespace FlaxEngine internal static uint GetTypeMTypesEnum(ManagedHandle typeHandle) { Type type = Unsafe.As(typeHandle.Target); + if (type.IsByRef) + type = type.GetElementType(); // Drop reference type (&) to get actual value type MTypes monoType; switch (type) { @@ -2943,24 +2968,8 @@ namespace FlaxEngine monoType = MTypes.Ptr; break; case Type _ when type.IsEnum: - { - var elementType = type.GetEnumUnderlyingType(); - if (elementType == typeof(sbyte) || elementType == typeof(short)) - monoType = MTypes.I2; - else if (elementType == typeof(byte) || elementType == typeof(ushort)) - monoType = MTypes.U2; - else if (elementType == typeof(int)) - monoType = MTypes.I4; - else if (elementType == typeof(uint)) - monoType = MTypes.U4; - else if (elementType == typeof(long)) - monoType = MTypes.I8; - else if (elementType == typeof(ulong)) - monoType = MTypes.U8; - else - throw new Exception($"Unsupported type '{type.FullName}'"); + monoType = MTypes.Enum; break; - } case Type _ when type.IsArray: case Type _ when type == typeof(ManagedArray): monoType = MTypes.Array; diff --git a/Source/Engine/Engine/NativeInterop_Invoker.cs b/Source/Engine/Engine/NativeInterop_Invoker.cs index e7f4f5be2..456000340 100644 --- a/Source/Engine/Engine/NativeInterop_Invoker.cs +++ b/Source/Engine/Engine/NativeInterop_Invoker.cs @@ -250,7 +250,7 @@ namespace FlaxEngine deleg(instancePtr.Target, ref param1); // Marshal reference parameters back to original unmanaged references - if (types[0].IsByRef) MarshalHelper.ToNative(ref param1, param1Ptr); + if (param1Ptr != IntPtr.Zero && types[0].IsByRef) MarshalHelper.ToNative(ref param1, param1Ptr); return IntPtr.Zero; } @@ -299,8 +299,8 @@ namespace FlaxEngine deleg(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); + if (param1Ptr != IntPtr.Zero && types[0].IsByRef) MarshalHelper.ToNative(ref param1, param1Ptr); + if (param2Ptr != IntPtr.Zero && types[1].IsByRef) MarshalHelper.ToNative(ref param2, param2Ptr); return IntPtr.Zero; } @@ -353,9 +353,9 @@ namespace FlaxEngine deleg(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); + if (param1Ptr != IntPtr.Zero && types[0].IsByRef) MarshalHelper.ToNative(ref param1, param1Ptr); + if (param2Ptr != IntPtr.Zero && types[1].IsByRef) MarshalHelper.ToNative(ref param2, param2Ptr); + if (param3Ptr != IntPtr.Zero && types[2].IsByRef) MarshalHelper.ToNative(ref param3, param3Ptr); return IntPtr.Zero; } @@ -412,10 +412,10 @@ namespace FlaxEngine deleg(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); + if (param1Ptr != IntPtr.Zero && types[0].IsByRef) MarshalHelper.ToNative(ref param1, param1Ptr); + if (param2Ptr != IntPtr.Zero && types[1].IsByRef) MarshalHelper.ToNative(ref param2, param2Ptr); + if (param3Ptr != IntPtr.Zero && types[2].IsByRef) MarshalHelper.ToNative(ref param3, param3Ptr); + if (param4Ptr != IntPtr.Zero && types[3].IsByRef) MarshalHelper.ToNative(ref param4, param4Ptr); return IntPtr.Zero; } @@ -500,7 +500,7 @@ namespace FlaxEngine deleg(ref param1); // Marshal reference parameters back to original unmanaged references - if (types[0].IsByRef) MarshalHelper.ToNative(ref param1, param1Ptr); + if (param1Ptr != IntPtr.Zero && types[0].IsByRef) MarshalHelper.ToNative(ref param1, param1Ptr); return IntPtr.Zero; } @@ -549,8 +549,8 @@ namespace FlaxEngine 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); + if (param1Ptr != IntPtr.Zero && types[0].IsByRef) MarshalHelper.ToNative(ref param1, param1Ptr); + if (param2Ptr != IntPtr.Zero && types[1].IsByRef) MarshalHelper.ToNative(ref param2, param2Ptr); return IntPtr.Zero; } @@ -603,9 +603,9 @@ namespace FlaxEngine 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); + if (param1Ptr != IntPtr.Zero && types[0].IsByRef) MarshalHelper.ToNative(ref param1, param1Ptr); + if (param2Ptr != IntPtr.Zero && types[1].IsByRef) MarshalHelper.ToNative(ref param2, param2Ptr); + if (param3Ptr != IntPtr.Zero && types[2].IsByRef) MarshalHelper.ToNative(ref param3, param3Ptr); return IntPtr.Zero; } @@ -662,10 +662,10 @@ namespace FlaxEngine 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); + if (param1Ptr != IntPtr.Zero && types[0].IsByRef) MarshalHelper.ToNative(ref param1, param1Ptr); + if (param2Ptr != IntPtr.Zero && types[1].IsByRef) MarshalHelper.ToNative(ref param2, param2Ptr); + if (param3Ptr != IntPtr.Zero && types[2].IsByRef) MarshalHelper.ToNative(ref param3, param3Ptr); + if (param4Ptr != IntPtr.Zero && types[3].IsByRef) MarshalHelper.ToNative(ref param4, param4Ptr); return IntPtr.Zero; } @@ -750,7 +750,7 @@ namespace FlaxEngine TRet ret = deleg(instancePtr.Target, ref param1); // Marshal reference parameters back to original unmanaged references - if (types[0].IsByRef) MarshalHelper.ToNative(ref param1, param1Ptr); + if (param1Ptr != IntPtr.Zero && types[0].IsByRef) MarshalHelper.ToNative(ref param1, param1Ptr); return MarshalReturnValue(ref ret); } @@ -799,8 +799,8 @@ namespace FlaxEngine TRet ret = deleg(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); + if (param1Ptr != IntPtr.Zero && types[0].IsByRef) MarshalHelper.ToNative(ref param1, param1Ptr); + if (param2Ptr != IntPtr.Zero && types[1].IsByRef) MarshalHelper.ToNative(ref param2, param2Ptr); return MarshalReturnValue(ref ret); } @@ -853,9 +853,9 @@ namespace FlaxEngine TRet ret = deleg(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); + if (param1Ptr != IntPtr.Zero && types[0].IsByRef) MarshalHelper.ToNative(ref param1, param1Ptr); + if (param2Ptr != IntPtr.Zero && types[1].IsByRef) MarshalHelper.ToNative(ref param2, param2Ptr); + if (param3Ptr != IntPtr.Zero && types[2].IsByRef) MarshalHelper.ToNative(ref param3, param3Ptr); return MarshalReturnValue(ref ret); } @@ -912,10 +912,10 @@ namespace FlaxEngine TRet ret = deleg(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); + if (param1Ptr != IntPtr.Zero && types[0].IsByRef) MarshalHelper.ToNative(ref param1, param1Ptr); + if (param2Ptr != IntPtr.Zero && types[1].IsByRef) MarshalHelper.ToNative(ref param2, param2Ptr); + if (param3Ptr != IntPtr.Zero && types[2].IsByRef) MarshalHelper.ToNative(ref param3, param3Ptr); + if (param4Ptr != IntPtr.Zero && types[3].IsByRef) MarshalHelper.ToNative(ref param4, param4Ptr); return MarshalReturnValue(ref ret); } @@ -1000,7 +1000,7 @@ namespace FlaxEngine TRet ret = deleg(ref param1); // Marshal reference parameters back to original unmanaged references - if (types[0].IsByRef) MarshalHelper.ToNative(ref param1, param1Ptr); + if (param1Ptr != IntPtr.Zero && types[0].IsByRef) MarshalHelper.ToNative(ref param1, param1Ptr); return MarshalReturnValue(ref ret); } @@ -1049,8 +1049,8 @@ namespace FlaxEngine 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); + if (param1Ptr != IntPtr.Zero && types[0].IsByRef) MarshalHelper.ToNative(ref param1, param1Ptr); + if (param2Ptr != IntPtr.Zero && types[1].IsByRef) MarshalHelper.ToNative(ref param2, param2Ptr); return MarshalReturnValue(ref ret); } @@ -1103,9 +1103,9 @@ namespace FlaxEngine 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); + if (param1Ptr != IntPtr.Zero && types[0].IsByRef) MarshalHelper.ToNative(ref param1, param1Ptr); + if (param2Ptr != IntPtr.Zero && types[1].IsByRef) MarshalHelper.ToNative(ref param2, param2Ptr); + if (param3Ptr != IntPtr.Zero && types[2].IsByRef) MarshalHelper.ToNative(ref param3, param3Ptr); return MarshalReturnValue(ref ret); } @@ -1162,10 +1162,10 @@ namespace FlaxEngine 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); + if (param1Ptr != IntPtr.Zero && types[0].IsByRef) MarshalHelper.ToNative(ref param1, param1Ptr); + if (param2Ptr != IntPtr.Zero && types[1].IsByRef) MarshalHelper.ToNative(ref param2, param2Ptr); + if (param3Ptr != IntPtr.Zero && types[2].IsByRef) MarshalHelper.ToNative(ref param3, param3Ptr); + if (param4Ptr != IntPtr.Zero && types[3].IsByRef) MarshalHelper.ToNative(ref param4, param4Ptr); return MarshalReturnValue(ref ret); } diff --git a/Source/Engine/Scripting/BinaryModule.cpp b/Source/Engine/Scripting/BinaryModule.cpp index 3232605f8..6ffdb14a9 100644 --- a/Source/Engine/Scripting/BinaryModule.cpp +++ b/Source/Engine/Scripting/BinaryModule.cpp @@ -798,10 +798,11 @@ namespace return nullptr; } - bool VariantTypeEquals(const VariantType& type, MType* mType) + bool VariantTypeEquals(const VariantType& type, MType* mType, bool isOut = false) { MClass* mClass = MCore::Type::GetClass(mType); - if (MUtils::GetClass(type) != mClass) + MClass* variantClass = MUtils::GetClass(type); + if (variantClass != mClass) { // Hack for Vector2/3/4 which alias with Float2/3/4 or Double2/3/4 (depending on USE_LARGE_WORLDS) const auto& stdTypes = *StdTypesContainer::Instance(); @@ -840,7 +841,7 @@ MMethod* ManagedBinaryModule::FindMethod(MClass* mclass, const ScriptingTypeMeth auto& param = signature.Params[paramIdx]; MType* type = method->GetParameterType(paramIdx); if (param.IsOut != method->GetParameterIsOut(paramIdx) || - !VariantTypeEquals(param.Type, type)) + !VariantTypeEquals(param.Type, type, param.IsOut)) { isValid = false; break; @@ -1289,8 +1290,8 @@ bool ManagedBinaryModule::InvokeMethod(void* method, const Variant& instance, Sp if (paramTypeHandle) { auto& valueType = paramTypeHandle.GetType(); - MISSING_CODE("TODO: reimplement unpacking managed structure out parameter from C# call"); - //valueType.Struct.Unbox(paramValue.AsBlob.Data, (MObject*)((byte*)param - sizeof(MonoObject))); // TODO: fix this for dotnet7 + MObject* boxed = MCore::Object::Box(param, valueType.ManagedClass); + valueType.Struct.Unbox(paramValue.AsBlob.Data, boxed); } break; } diff --git a/Source/Engine/Scripting/ManagedCLR/MCore.h b/Source/Engine/Scripting/ManagedCLR/MCore.h index 93826afe3..ca1193dde 100644 --- a/Source/Engine/Scripting/ManagedCLR/MCore.h +++ b/Source/Engine/Scripting/ManagedCLR/MCore.h @@ -147,6 +147,7 @@ public: { static ::String ToString(MType* type); static MClass* GetClass(MType* type); + static MType* GetElementType(MType* type); static int32 GetSize(MType* type); static MTypes GetType(MType* type); static bool IsPointer(MType* type); diff --git a/Source/Engine/Scripting/ManagedCLR/MUtils.cpp b/Source/Engine/Scripting/ManagedCLR/MUtils.cpp index 1022ab5a1..b7c0b31c1 100644 --- a/Source/Engine/Scripting/ManagedCLR/MUtils.cpp +++ b/Source/Engine/Scripting/ManagedCLR/MUtils.cpp @@ -1166,6 +1166,12 @@ void* MUtils::VariantToManagedArgPtr(Variant& value, MType* type, bool& failed) } } break; + case MTypes::Enum: + { + if (value.Type.Type != VariantType::Enum) + return nullptr; + return &value.AsUint64; + } case MTypes::Class: { if (value.Type.Type == VariantType::Null) @@ -1183,7 +1189,8 @@ void* MUtils::VariantToManagedArgPtr(Variant& value, MType* type, bool& failed) if (value.Type.Type != VariantType::Array) return nullptr; MObject* object = BoxVariant(value); - if (object && !MCore::Object::GetClass(object)->IsSubClassOf(MCore::Array::GetClass(MCore::Type::GetClass(type)))) + auto typeStr = MCore::Type::ToString(type); + if (object && !MCore::Object::GetClass(object)->IsSubClassOf(MCore::Type::GetClass(type))) object = nullptr; return object; } diff --git a/Source/Engine/Scripting/Runtime/DotNet.cpp b/Source/Engine/Scripting/Runtime/DotNet.cpp index d50865988..c1bc1e7f4 100644 --- a/Source/Engine/Scripting/Runtime/DotNet.cpp +++ b/Source/Engine/Scripting/Runtime/DotNet.cpp @@ -525,9 +525,17 @@ MObject* MCore::Exception::GetNotSupported(const char* msg) MClass* MCore::Type::GetClass(MType* type) { + static void* GetTypeClassPtr = GetStaticMethodPointer(TEXT("GetTypeClass")); + type = (MType*)CallStaticMethod(GetTypeClassPtr, type); return GetOrCreateClass(type); } +MType* MCore::Type::GetElementType(MType* type) +{ + static void* GetElementClassPtr = GetStaticMethodPointer(TEXT("GetElementClass")); + return (MType*)CallStaticMethod(GetElementClassPtr, type); +} + int32 MCore::Type::GetSize(MType* type) { return GetOrCreateClass(type)->GetInstanceSize(); @@ -546,14 +554,14 @@ MTypes MCore::Type::GetType(MType* type) bool MCore::Type::IsPointer(MType* type) { - MISSING_CODE("TODO: MCore::Type::IsPointer"); // TODO: MCore::Type::IsPointer - return false; + static void* GetTypeIsPointerPtr = GetStaticMethodPointer(TEXT("GetTypeIsPointer")); + return CallStaticMethod(GetTypeIsPointerPtr, type); } bool MCore::Type::IsReference(MType* type) { - MISSING_CODE("TODO: MCore::Type::IsReference"); // TODO: MCore::Type::IsReference - return false; + static void* GetTypeIsReferencePtr = GetStaticMethodPointer(TEXT("GetTypeIsReference")); + return CallStaticMethod(GetTypeIsReferencePtr, type); } const MAssembly::ClassesDictionary& MAssembly::GetClasses() const @@ -1256,7 +1264,7 @@ void* MMethod::GetThunk() MMethod* MMethod::InflateGeneric() const { - // TODO: implement/test this on .NET (eg. C# script that inherits other generic C# script which implements C++ native method) + // This seams to be unused on .NET (Mono required inflating generic class of the script) return const_cast(this); } @@ -1355,13 +1363,16 @@ MMethod* MProperty::GetSetMethod() const MObject* MProperty::GetValue(MObject* instance, MObject** exception) const { - MISSING_CODE("TODO: MProperty::GetValue"); // TODO: MProperty::GetValue - return nullptr; + CHECK_RETURN(_getMethod, nullptr); + return _getMethod->Invoke(instance, nullptr, exception); } void MProperty::SetValue(MObject* instance, void* value, MObject** exception) const { - MISSING_CODE("TODO: MProperty::SetValue"); // TODO: MProperty::SetValue + CHECK(_setMethod); + void* params[1]; + params[0] = value; + _setMethod->Invoke(instance, params, exception); } bool MProperty::HasAttribute(MClass* monoClass) const diff --git a/Source/Engine/Scripting/Runtime/Mono.cpp b/Source/Engine/Scripting/Runtime/Mono.cpp index f34fb4b87..363acdefb 100644 --- a/Source/Engine/Scripting/Runtime/Mono.cpp +++ b/Source/Engine/Scripting/Runtime/Mono.cpp @@ -928,6 +928,12 @@ MClass* MCore::Type::GetClass(MType* type) return FindClass(mclass); } +MType* MCore::Type::GetElementType(MType* type) +{ + CRASH; // impl this (get type class and call GetElementClass) + return nullptr; +} + int32 MCore::Type::GetSize(MType* type) { int32 valueAlignment;