From aaaf7c5c3713cbb940eb859f988f1426c1757293 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 6 Mar 2023 16:17:52 +0100 Subject: [PATCH] Various fixes to scripting --- Source/Engine/Engine/NativeInterop.cs | 8 +- Source/Engine/Scripting/DotNet/CoreCLR.h | 3 + Source/Engine/Scripting/DotNet/MonoApi.cpp | 7 +- Source/Engine/Scripting/InternalCalls.h | 1 + Source/Engine/Scripting/ScriptingObject.cpp | 8 +- Source/Engine/Scripting/ScriptingObject.h | 4 +- .../java/com/flaxengine/GameActivity.java | 2 +- .../Bindings/BindingsGenerator.Cpp.cs | 127 +++++++----------- 8 files changed, 73 insertions(+), 87 deletions(-) diff --git a/Source/Engine/Engine/NativeInterop.cs b/Source/Engine/Engine/NativeInterop.cs index ad04a623d..f5a0100aa 100644 --- a/Source/Engine/Engine/NativeInterop.cs +++ b/Source/Engine/Engine/NativeInterop.cs @@ -783,7 +783,6 @@ namespace FlaxEngine handle.Free(); } } - } [CustomMarshaller(typeof(Dictionary<,>), MarshalMode.ManagedToUnmanagedIn, typeof(DictionaryMarshaller<,>.ManagedToNative))] @@ -2517,7 +2516,12 @@ namespace FlaxEngine { object fieldOwner = fieldOwnerHandle.Target; FieldHolder field = Unsafe.As(fieldHandle.Target); - field.field.SetValue(fieldOwner, Marshal.PtrToStructure(valuePtr, field.field.FieldType)); + object value = null; + if (field.field.FieldType.IsValueType) + value = Marshal.PtrToStructure(valuePtr, field.field.FieldType); + else if (valuePtr != IntPtr.Zero) + value = ManagedHandle.FromIntPtr(valuePtr).Target; + field.field.SetValue(fieldOwner, value); } [UnmanagedCallersOnly] diff --git a/Source/Engine/Scripting/DotNet/CoreCLR.h b/Source/Engine/Scripting/DotNet/CoreCLR.h index 2bd15e15b..5fe2b8691 100644 --- a/Source/Engine/Scripting/DotNet/CoreCLR.h +++ b/Source/Engine/Scripting/DotNet/CoreCLR.h @@ -16,6 +16,9 @@ #define FLAX_CORECLR_STRING StringAnsi #endif +/// +/// .NET Runtime hosting library that uses Hostfxr (https://github.com/dotnet/runtime). +/// class CoreCLR { public: diff --git a/Source/Engine/Scripting/DotNet/MonoApi.cpp b/Source/Engine/Scripting/DotNet/MonoApi.cpp index 9e1e6d85b..b8ac4db28 100644 --- a/Source/Engine/Scripting/DotNet/MonoApi.cpp +++ b/Source/Engine/Scripting/DotNet/MonoApi.cpp @@ -852,18 +852,21 @@ MONO_API MONO_RT_EXTERNAL_ONLY MonoObject* mono_property_get_value(MonoProperty* MONO_API void mono_gc_wbarrier_set_field(MonoObject* obj, void* field_ptr, MonoObject* value) { - ASSERT(false); + CRASH; // Not supported on CoreCRL } + MONO_API void mono_gc_wbarrier_set_arrayref(MonoArray* arr, void* slot_ptr, MonoObject* 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) { // Ignored - *((void**)ptr) = value; + *(void**)ptr = value; } + MONO_API void mono_gc_wbarrier_value_copy(void* dest, /*const*/ void* src, int count, MonoClass* klass) { // Ignored diff --git a/Source/Engine/Scripting/InternalCalls.h b/Source/Engine/Scripting/InternalCalls.h index cb6df4c9d..b867ee5c0 100644 --- a/Source/Engine/Scripting/InternalCalls.h +++ b/Source/Engine/Scripting/InternalCalls.h @@ -85,6 +85,7 @@ extern "C" FLAXENGINE_API void mono_add_internal_call(const char* name, const vo #else #define ADD_INTERNAL_CALL(fullName, method) +#define DEFINE_INTERNAL_CALL(returnType) static returnType #define INTERNAL_CALL_CHECK(obj) #define INTERNAL_CALL_CHECK_EXP(expression) #define INTERNAL_CALL_CHECK_RETURN(obj, defaultValue) diff --git a/Source/Engine/Scripting/ScriptingObject.cpp b/Source/Engine/Scripting/ScriptingObject.cpp index e4ffbffae..61717b8ef 100644 --- a/Source/Engine/Scripting/ScriptingObject.cpp +++ b/Source/Engine/Scripting/ScriptingObject.cpp @@ -221,12 +221,12 @@ void ScriptingObject::ChangeID(const Guid& newId) _type.GetType().Module->OnObjectIdChanged(this, prevId); } -void ScriptingObject::SetManagedInstance(MonoObject* instance) +void ScriptingObject::SetManagedInstance(MObject* instance) { ASSERT(_gcHandle == 0); #if USE_NETCORE _gcHandle = (MGCHandle)instance; -#else +#elif !COMPILE_WITHOUT_CSHARP _gcHandle = MUtils::NewGCHandle(instance, false); #endif } @@ -454,12 +454,12 @@ ManagedScriptingObject::ManagedScriptingObject(const SpawnParams& params) { } -void ManagedScriptingObject::SetManagedInstance(MonoObject* instance) +void ManagedScriptingObject::SetManagedInstance(MObject* instance) { ASSERT(_gcHandle == 0); #if USE_NETCORE _gcHandle = (MGCHandle)instance; -#else +#elif !COMPILE_WITHOUT_CSHARP _gcHandle = MUtils::NewGCHandleWeakref(instance, false); #endif } diff --git a/Source/Engine/Scripting/ScriptingObject.h b/Source/Engine/Scripting/ScriptingObject.h index 160203e0a..a75ab539c 100644 --- a/Source/Engine/Scripting/ScriptingObject.h +++ b/Source/Engine/Scripting/ScriptingObject.h @@ -190,7 +190,7 @@ public: virtual void ChangeID(const Guid& newId); public: - virtual void SetManagedInstance(MonoObject* instance); + virtual void SetManagedInstance(MObject* instance); virtual void OnManagedInstanceDeleted(); virtual void OnScriptingDispose(); @@ -246,7 +246,7 @@ public: public: // [ScriptingObject] - void SetManagedInstance(MonoObject* instance) override; + void SetManagedInstance(MObject* instance) override; void OnManagedInstanceDeleted() override; void OnScriptingDispose() override; bool CreateManaged() override; diff --git a/Source/Platforms/Android/Binaries/Project/app/src/main/java/com/flaxengine/GameActivity.java b/Source/Platforms/Android/Binaries/Project/app/src/main/java/com/flaxengine/GameActivity.java index 9332b36f7..8567030b5 100644 --- a/Source/Platforms/Android/Binaries/Project/app/src/main/java/com/flaxengine/GameActivity.java +++ b/Source/Platforms/Android/Binaries/Project/app/src/main/java/com/flaxengine/GameActivity.java @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. +// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. package com.flaxengine; diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs index 2dddd57d1..56e4fecea 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs @@ -578,7 +578,7 @@ namespace Flax.Build.Bindings // Managed class if (apiType.IsClass) { - // Use wrapper structure that represents the memory layout of the managed data + // Use wrapper that box the data into managed object if (!CppUsedNonPodTypes.Contains(apiType)) { CppUsedNonPodTypes.Add(apiType); @@ -784,7 +784,7 @@ namespace Flax.Build.Bindings // Managed class if (apiType.IsClass && !apiType.IsScriptingObject) { - // Use wrapper structure that represents the memory layout of the managed data + // Use wrapper that unboxes the data into managed object if (!CppUsedNonPodTypes.Contains(apiType)) { CppUsedNonPodTypes.Add(apiType); @@ -2152,10 +2152,12 @@ namespace Flax.Build.Bindings } if (useScripting && useCSharp) { +#if !USE_NETCORE foreach (var e in CppInternalCalls) { contents.AppendLine($" ADD_INTERNAL_CALL(\"{classTypeNameManagedInternalCall}::Internal_{e.Key}\", &{e.Value});"); } +#endif } GenerateCppTypeInitRuntime?.Invoke(buildData, classInfo, contents); @@ -2306,10 +2308,12 @@ namespace Flax.Build.Bindings if (useCSharp) { +#if !USE_NETCORE foreach (var e in CppInternalCalls) { contents.AppendLine($" ADD_INTERNAL_CALL(\"{structureTypeNameManagedInternalCall}::Internal_{e.Key}\", &{e.Value});"); } +#endif } GenerateCppTypeInitRuntime?.Invoke(buildData, structureInfo, contents); @@ -2809,35 +2813,35 @@ namespace Flax.Build.Bindings // Get the full typename with nested parent prefix var fullName = apiType.FullNameNative; - // Generate managed type memory layout - header.Append("struct ").Append(apiType.Name).Append("Managed").AppendLine(); - header.Append('{').AppendLine(); - if (classInfo != null) - header.AppendLine(" MonoObject obj;"); - for (var i = 0; i < fields.Count; i++) - { - var fieldInfo = fields[i]; - if (fieldInfo.IsStatic) - continue; - string type; - - if (fieldInfo.NoArray && fieldInfo.Type.IsArray) - { - // Fixed-size array that needs to be inlined into structure instead of passing it as managed array - fieldInfo.Type.IsArray = false; - CppParamsWrappersCache[i] = GenerateCppWrapperNativeToManaged(buildData, fieldInfo.Type, apiType, out type, null); - fieldInfo.Type.IsArray = true; - header.AppendFormat(" {0} {1}[{2}];", type, fieldInfo.Name, fieldInfo.Type.ArraySize).AppendLine(); - continue; - } - - CppParamsWrappersCache[i] = GenerateCppWrapperNativeToManaged(buildData, fieldInfo.Type, apiType, out type, null); - header.AppendFormat(" {0} {1};", type, fieldInfo.Name).AppendLine(); - } - header.Append('}').Append(';').AppendLine(); - if (structureInfo != null) { + // Generate managed type memory layout + header.Append("struct ").Append(apiType.Name).Append("Managed").AppendLine(); + header.Append('{').AppendLine(); + if (classInfo != null) + header.AppendLine(" MonoObject obj;"); + for (var i = 0; i < fields.Count; i++) + { + var fieldInfo = fields[i]; + if (fieldInfo.IsStatic) + continue; + string type; + + if (fieldInfo.NoArray && fieldInfo.Type.IsArray) + { + // Fixed-size array that needs to be inlined into structure instead of passing it as managed array + fieldInfo.Type.IsArray = false; + CppParamsWrappersCache[i] = GenerateCppWrapperNativeToManaged(buildData, fieldInfo.Type, apiType, out type, null); + fieldInfo.Type.IsArray = true; + header.AppendFormat(" {0} {1}[{2}];", type, fieldInfo.Name, fieldInfo.Type.ArraySize).AppendLine(); + continue; + } + + CppParamsWrappersCache[i] = GenerateCppWrapperNativeToManaged(buildData, fieldInfo.Type, apiType, out type, null); + header.AppendFormat(" {0} {1};", type, fieldInfo.Name).AppendLine(); + } + header.Append('}').Append(';').AppendLine(); + // Generate forward declarations of structure converting functions header.AppendLine(); header.AppendLine("namespace {"); @@ -2845,7 +2849,7 @@ namespace Flax.Build.Bindings header.AppendFormat("{1} ToNative(const {0}Managed& value);", apiType.Name, fullName).AppendLine(); header.AppendLine("}"); - // Generate MConverter + // Generate MConverter for a structure header.Append("template<>").AppendLine(); header.AppendFormat("struct MConverter<{0}>", fullName).AppendLine(); header.Append('{').AppendLine(); @@ -2955,38 +2959,29 @@ namespace Flax.Build.Bindings } else if (classInfo != null) { - // Generate MConverter + // Generate MConverter for a class header.Append("template<>").AppendLine(); header.AppendFormat("struct MConverter<{0}>", fullName).AppendLine(); header.Append('{').AppendLine(); header.AppendFormat(" static MonoObject* Box(const {0}& data, MonoClass* klass)", fullName).AppendLine(); header.Append(" {").AppendLine(); - header.AppendFormat(" auto obj = ({0}Managed*)mono_object_new(mono_domain_get(), klass);", fullName).AppendLine(); + header.Append(" MonoObject* obj = mono_object_new(mono_domain_get(), klass);").AppendLine(); for (var i = 0; i < fields.Count; i++) { var fieldInfo = fields[i]; if (fieldInfo.IsStatic || fieldInfo.IsConstexpr) continue; - if (fieldInfo.NoArray && fieldInfo.Type.IsArray) - { - // Fixed-size array needs to unbox every item manually if not using managed array - if (fieldInfo.Type.IsPod(buildData, apiType)) - header.AppendFormat(" Platform::MemoryCopy(obj->{0}, data.{0}, sizeof({2}) * {1});", fieldInfo.Name, fieldInfo.Type.ArraySize, fieldInfo.Type).AppendLine(); - else - header.AppendFormat(" for (int32 i = 0; i < {0}; i++)", fieldInfo.Type.ArraySize).AppendLine().AppendFormat(" obj->{0}[i] = data.{0}[i];", fieldInfo.Name).AppendLine(); - continue; - } - - var wrapper = CppParamsWrappersCache[i]; + var fieldType = FindApiTypeInfo(buildData, fieldInfo.Type, apiType); + if (fieldInfo == null || fieldType.IsValueType) // TODO: support any value type (eg. by boxing) + throw new Exception($"Not supported field {fieldInfo.Type} {fieldInfo.Name} in class {classInfo.Name}."); + + var wrapper = GenerateCppWrapperNativeToManaged(buildData, fieldInfo.Type, apiType, out var type, null); var value = string.IsNullOrEmpty(wrapper) ? "data." + fieldInfo.Name : string.Format(wrapper, "data." + fieldInfo.Name); - if (fieldInfo.Type.IsPod(buildData, apiType)) - header.AppendFormat(" obj->{0} = {1};", fieldInfo.Name, value).AppendLine(); - else - header.AppendFormat(" MONO_OBJECT_SETREF(obj, {0}, {1});", fieldInfo.Name, value).AppendLine(); + header.AppendFormat(" mono_field_set_value(obj, mono_class_get_field_from_name(klass, \"{0}\"), {1});", fieldInfo.Name, value).AppendLine(); } - header.Append(" return (MonoObject*)obj;").AppendLine(); + header.Append(" return obj;").AppendLine(); header.Append(" }").AppendLine(); header.AppendFormat(" static MonoObject* Box(const {0}& data)", fullName).AppendLine(); @@ -2995,9 +2990,10 @@ namespace Flax.Build.Bindings header.Append(" return Box(data, klass);").AppendLine(); header.Append(" }").AppendLine(); - header.AppendFormat(" static void Unbox({0}& result, MonoObject* data)", fullName).AppendLine(); + header.AppendFormat(" static void Unbox({0}& result, MonoObject* obj)", fullName).AppendLine(); header.Append(" {").AppendLine(); - header.AppendFormat(" auto obj = ({0}Managed*)data;", fullName).AppendLine(); + header.Append(" MonoClass* klass = mono_object_get_class(obj);").AppendLine(); + header.Append(" void* v = nullptr;").AppendLine(); for (var i = 0; i < fields.Count; i++) { var fieldInfo = fields[i]; @@ -3005,33 +3001,12 @@ namespace Flax.Build.Bindings continue; CppNonPodTypesConvertingGeneration = true; - var wrapper = GenerateCppWrapperManagedToNative(buildData, fieldInfo.Type, apiType, out _, out _, null, out _); + var wrapper = GenerateCppWrapperManagedToNative(buildData, fieldInfo.Type, apiType, out var type, out _, null, out _); CppNonPodTypesConvertingGeneration = false; - if (fieldInfo.Type.IsArray) - { - // Fixed-size array needs to unbox every item manually - if (fieldInfo.NoArray) - { - if (fieldInfo.Type.IsPod(buildData, apiType)) - header.AppendFormat(" Platform::MemoryCopy(result.{0}, obj->{0}, sizeof({2}) * {1});", fieldInfo.Name, fieldInfo.Type.ArraySize, fieldInfo.Type).AppendLine(); - else - header.AppendFormat(" for (int32 i = 0; i < {0}; i++)", fieldInfo.Type.ArraySize).AppendLine().AppendFormat(" result.{0}[i] = obj->{0}[i];", fieldInfo.Name).AppendLine(); - } - else - { - wrapper = string.Format(wrapper.Remove(wrapper.Length - 6), string.Format("obj->{0}", fieldInfo.Name)); - header.AppendFormat(" auto tmp{0} = {1};", fieldInfo.Name, wrapper).AppendLine(); - header.AppendFormat(" for (int32 i = 0; i < {0} && i < tmp{1}.Count(); i++)", fieldInfo.Type.ArraySize, fieldInfo.Name).AppendLine(); - header.AppendFormat(" result.{0}[i] = tmp{0}[i];", fieldInfo.Name).AppendLine(); - } - continue; - } - - if (string.IsNullOrEmpty(wrapper)) - header.AppendFormat(" result.{0} = obj->{0};", fieldInfo.Name).AppendLine(); - else - header.AppendFormat(" result.{0} = {1};", fieldInfo.Name, string.Format(wrapper, string.Format("obj->{0}", fieldInfo.Name))).AppendLine(); + header.AppendFormat(" mono_field_get_value(obj, mono_class_get_field_from_name(klass, \"{0}\"), &v);", fieldInfo.Name).AppendLine(); + var value = $"({type})v"; + header.AppendFormat(" result.{0} = {1};", fieldInfo.Name, string.IsNullOrEmpty(wrapper) ? value : string.Format(wrapper, value)).AppendLine(); } header.Append(" }").AppendLine(); @@ -3052,7 +3027,7 @@ namespace Flax.Build.Bindings header.AppendFormat(" void ToNativeArray(Array<{0}, AllocationType>& result, MonoArray* data, int32 length)", fullName).AppendLine(); header.Append(" {").AppendLine(); header.Append(" for (int32 i = 0; i < length; i++)").AppendLine(); - header.AppendFormat(" Unbox(result[i], (MonoObject*)mono_array_addr_with_size(data, sizeof({0}Managed), length));", fullName).AppendLine(); + header.AppendFormat(" Unbox(result[i], (MonoObject*)mono_array_addr_with_size(data, sizeof(MonoObject*), length));", fullName).AppendLine(); header.Append(" }").AppendLine(); header.Append('}').Append(';').AppendLine();