diff --git a/Source/Engine/Engine/NativeInterop.Unmanaged.cs b/Source/Engine/Engine/NativeInterop.Unmanaged.cs index 7c1e2e64a..7af32fc9b 100644 --- a/Source/Engine/Engine/NativeInterop.Unmanaged.cs +++ b/Source/Engine/Engine/NativeInterop.Unmanaged.cs @@ -835,7 +835,7 @@ namespace FlaxEngine.Interop { object fieldOwner = fieldOwnerHandle.Target; IntPtr fieldRef; -#if USE_AOT +#if USE_AOT || DOTNET_HOST_MONO FieldHolder field = Unsafe.As(fieldHandle.Target); fieldRef = IntPtr.Zero; Debug.LogError("Not supported FieldGetValueReference"); diff --git a/Source/Engine/Engine/NativeInterop.cs b/Source/Engine/Engine/NativeInterop.cs index 7d16b4752..745a01c2f 100644 --- a/Source/Engine/Engine/NativeInterop.cs +++ b/Source/Engine/Engine/NativeInterop.cs @@ -107,17 +107,13 @@ namespace FlaxEngine.Interop { } -#if !USE_AOT +#if !USE_AOT && !DOTNET_HOST_MONO [UnsafeAccessor(UnsafeAccessorKind.Field, Name = "__unmanagedPtr")] extern static ref IntPtr GetUnmanagedPtrFieldReference(Object obj); [UnsafeAccessor(UnsafeAccessorKind.Field, Name = "__internalId")] extern static ref Guid GetInternalIdFieldReference(Object obj); - // Cache offsets to frequently accessed fields of FlaxEngine.Object - private static int unmanagedPtrFieldOffset = IntPtr.Size + (Unsafe.Read((typeof(FlaxEngine.Object).GetField("__unmanagedPtr", BindingFlags.Instance | BindingFlags.NonPublic).FieldHandle.Value + 4 + IntPtr.Size).ToPointer()) & 0xFFFFFF); - private static int internalIdFieldOffset = IntPtr.Size + (Unsafe.Read((typeof(FlaxEngine.Object).GetField("__internalId", BindingFlags.Instance | BindingFlags.NonPublic).FieldHandle.Value + 4 + IntPtr.Size).ToPointer()) & 0xFFFFFF); - [UnmanagedCallersOnly] internal static void ScriptingObjectSetInternalValues(ManagedHandle objectHandle, IntPtr unmanagedPtr, IntPtr idPtr) { @@ -418,14 +414,21 @@ namespace FlaxEngine.Interop if (field.IsLiteral) return 0; +#if DOTNET_HOST_CORECLR // Get the address of the field, source: https://stackoverflow.com/a/56512720 int fieldOffset = Unsafe.Read((field.FieldHandle.Value + 4 + IntPtr.Size).ToPointer()) & 0xFFFFFF; if (!type.IsValueType) fieldOffset += IntPtr.Size; return fieldOffset; +#elif DOTNET_HOST_MONO + // Get the address from _MonoClassField::offset, source: https://meetemq.com/2023/09/10/nets-fields-and-their-offsets/ + IntPtr fieldOffsetPtr = (IntPtr*)field.FieldHandle.Value; // Pointer to MonoClassField + fieldOffsetPtr += 3; // Skip three pointers (type, name, parent_and_flags) + return *(int*)fieldOffsetPtr - IntPtr.Size * 2; // Load the value of a pointer (4 bytes, int32), then subtracting 16 bytes from it (2 pointers for vtable and threadsync) +#endif } -#if USE_AOT +#if USE_AOT || DOTNET_HOST_MONO /// /// Helper utility to set field of the referenced value via reflection. /// @@ -756,12 +759,12 @@ namespace FlaxEngine.Interop } } } - throw new NativeInteropException($"Invalid field with offset {fieldOffset} to marshal for type {typeof(T).Name}"); + throw new NativeInteropException($"Invalid field with offset {fieldOffset} to marshal for type {typeof(T).FullName}"); } private static void ToManagedFieldPointerValueType(FieldInfo field, int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : struct { -#if USE_AOT +#if USE_AOT || DOTNET_HOST_MONO IntPtr fieldValue = Unsafe.Read(nativeFieldPtr.ToPointer()); FieldHelper.SetReferenceTypeField(field, ref fieldOwner, fieldValue); #else @@ -773,7 +776,7 @@ namespace FlaxEngine.Interop private static void ToManagedFieldPointerReferenceType(FieldInfo field, int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : class { -#if USE_AOT +#if USE_AOT || DOTNET_HOST_MONO IntPtr fieldValue = Unsafe.Read(nativeFieldPtr.ToPointer()); FieldHelper.SetReferenceTypeField(field, ref fieldOwner, fieldValue); #else @@ -785,7 +788,7 @@ namespace FlaxEngine.Interop private static void ToNativeFieldPointerValueType(FieldInfo field, int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : struct { -#if USE_AOT +#if USE_AOT || DOTNET_HOST_MONO object boxed = field.GetValue(fieldOwner); IntPtr fieldValue = new IntPtr(Pointer.Unbox(boxed)); #else @@ -797,7 +800,7 @@ namespace FlaxEngine.Interop private static void ToNativeFieldPointerReferenceType(FieldInfo field, int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : class { -#if USE_AOT +#if USE_AOT || DOTNET_HOST_MONO object boxed = field.GetValue(fieldOwner); IntPtr fieldValue = new IntPtr(Pointer.Unbox(boxed)); #else @@ -845,13 +848,13 @@ namespace FlaxEngine.Interop fieldSize += (nativeFieldPtr - fieldStartPtr).ToInt32(); } -#if USE_AOT +#if USE_AOT || DOTNET_HOST_MONO TField fieldValue = default; #else ref TField fieldValue = ref FieldHelper.GetValueTypeFieldReference(fieldOffset, ref fieldOwner); #endif MarshalHelper.ToManaged(ref fieldValue, nativeFieldPtr, false); -#if USE_AOT +#if USE_AOT || DOTNET_HOST_MONO FieldHelper.SetReferenceTypeField(field, ref fieldOwner, fieldValue); #endif } @@ -866,13 +869,13 @@ namespace FlaxEngine.Interop fieldSize += (nativeFieldPtr - fieldStartPtr).ToInt32(); } -#if USE_AOT +#if USE_AOT || DOTNET_HOST_MONO TField fieldValue = default; #else ref TField fieldValue = ref FieldHelper.GetReferenceTypeFieldReference(fieldOffset, ref fieldOwner); #endif MarshalHelper.ToManaged(ref fieldValue, nativeFieldPtr, false); -#if USE_AOT +#if USE_AOT || DOTNET_HOST_MONO FieldHelper.SetReferenceTypeField(field, ref fieldOwner, fieldValue); #endif } @@ -885,13 +888,13 @@ namespace FlaxEngine.Interop nativeFieldPtr = EnsureAlignment(nativeFieldPtr, IntPtr.Size); fieldSize += (nativeFieldPtr - fieldStartPtr).ToInt32(); -#if USE_AOT +#if USE_AOT || DOTNET_HOST_MONO TField[] fieldValue = (TField[])field.GetValue(fieldOwner); #else ref TField[] fieldValue = ref FieldHelper.GetValueTypeFieldReference(fieldOffset, ref fieldOwner); #endif MarshalHelper.ToManaged(ref fieldValue, Unsafe.Read(nativeFieldPtr.ToPointer()), false); -#if USE_AOT +#if USE_AOT || DOTNET_HOST_MONO FieldHelper.SetReferenceTypeField(field, ref fieldOwner, fieldValue); #endif } @@ -904,13 +907,13 @@ namespace FlaxEngine.Interop nativeFieldPtr = EnsureAlignment(nativeFieldPtr, IntPtr.Size); fieldSize += (nativeFieldPtr - fieldStartPtr).ToInt32(); -#if USE_AOT +#if USE_AOT || DOTNET_HOST_MONO TField[] fieldValue = null; #else ref TField[] fieldValue = ref FieldHelper.GetReferenceTypeFieldReference(fieldOffset, ref fieldOwner); #endif MarshalHelper.ToManaged(ref fieldValue, Unsafe.Read(nativeFieldPtr.ToPointer()), false); -#if USE_AOT +#if USE_AOT || DOTNET_HOST_MONO FieldHelper.SetReferenceTypeField(field, ref fieldOwner, fieldValue); #endif } @@ -925,7 +928,7 @@ namespace FlaxEngine.Interop fieldSize += (nativeFieldPtr - startPtr).ToInt32(); } -#if USE_AOT +#if USE_AOT || DOTNET_HOST_MONO TField fieldValue = (TField)field.GetValue(fieldOwner); #else ref TField fieldValue = ref FieldHelper.GetValueTypeFieldReference(fieldOffset, ref fieldOwner); @@ -978,13 +981,13 @@ namespace FlaxEngine.Interop nativeFieldPtr = EnsureAlignment(nativeFieldPtr, IntPtr.Size); fieldSize += (nativeFieldPtr - fieldStartPtr).ToInt32(); -#if USE_AOT +#if USE_AOT || DOTNET_HOST_MONO TField fieldValue = null; #else ref TField fieldValue = ref FieldHelper.GetValueTypeFieldReference(fieldOffset, ref fieldOwner); #endif MarshalHelper.ToManaged(ref fieldValue, Unsafe.Read(nativeFieldPtr.ToPointer()), false); -#if USE_AOT +#if USE_AOT || DOTNET_HOST_MONO FieldHelper.SetReferenceTypeField(field, ref fieldOwner, fieldValue); #endif } @@ -996,13 +999,13 @@ namespace FlaxEngine.Interop nativeFieldPtr = EnsureAlignment(nativeFieldPtr, IntPtr.Size); fieldSize += (nativeFieldPtr - fieldStartPtr).ToInt32(); -#if USE_AOT +#if USE_AOT || DOTNET_HOST_MONO TField fieldValue = default; #else ref TField fieldValue = ref FieldHelper.GetReferenceTypeFieldReference(fieldOffset, ref fieldOwner); #endif MarshalHelper.ToManaged(ref fieldValue, Unsafe.Read(nativeFieldPtr.ToPointer()), false); -#if USE_AOT +#if USE_AOT || DOTNET_HOST_MONO FieldHelper.SetReferenceTypeField(field, ref fieldOwner, fieldValue); #endif } @@ -1014,13 +1017,13 @@ namespace FlaxEngine.Interop nativeFieldPtr = EnsureAlignment(nativeFieldPtr, IntPtr.Size); fieldSize += (nativeFieldPtr - fieldStartPtr).ToInt32(); -#if USE_AOT +#if USE_AOT || DOTNET_HOST_MONO TField[] fieldValue = null; #else ref TField[] fieldValue = ref FieldHelper.GetValueTypeFieldReference(fieldOffset, ref fieldOwner); #endif MarshalHelper.ToManaged(ref fieldValue, Unsafe.Read(nativeFieldPtr.ToPointer()), false); -#if USE_AOT +#if USE_AOT || DOTNET_HOST_MONO FieldHelper.SetReferenceTypeField(field, ref fieldOwner, fieldValue); #endif } @@ -1032,13 +1035,13 @@ namespace FlaxEngine.Interop nativeFieldPtr = EnsureAlignment(nativeFieldPtr, IntPtr.Size); fieldSize += (nativeFieldPtr - fieldStartPtr).ToInt32(); -#if USE_AOT +#if USE_AOT || DOTNET_HOST_MONO TField[] fieldValue = null; #else ref TField[] fieldValue = ref FieldHelper.GetReferenceTypeFieldReference(fieldOffset, ref fieldOwner); #endif MarshalHelper.ToManaged(ref fieldValue, Unsafe.Read(nativeFieldPtr.ToPointer()), false); -#if USE_AOT +#if USE_AOT || DOTNET_HOST_MONO FieldHelper.SetReferenceTypeField(field, ref fieldOwner, fieldValue); #endif } @@ -1050,7 +1053,7 @@ namespace FlaxEngine.Interop nativeFieldPtr = EnsureAlignment(nativeFieldPtr, IntPtr.Size); fieldSize += (nativeFieldPtr - fieldStartPtr).ToInt32(); -#if USE_AOT +#if USE_AOT || DOTNET_HOST_MONO TField fieldValue = (TField)field.GetValue(fieldOwner); #else ref TField fieldValue = ref FieldHelper.GetValueTypeFieldReference(fieldOffset, ref fieldOwner); @@ -1065,7 +1068,7 @@ namespace FlaxEngine.Interop nativeFieldPtr = EnsureAlignment(nativeFieldPtr, IntPtr.Size); fieldSize += (nativeFieldPtr - fieldStartPtr).ToInt32(); -#if USE_AOT +#if USE_AOT || DOTNET_HOST_MONO TField fieldValue = (TField)field.GetValue(fieldOwner); #else ref TField fieldValue = ref FieldHelper.GetReferenceTypeFieldReference(fieldOffset, ref fieldOwner); @@ -1439,7 +1442,7 @@ namespace FlaxEngine.Interop return RuntimeHelpers.GetUninitializedObject(wrappedType); } -#if !USE_AOT +#if !USE_AOT && !DOTNET_HOST_MONO internal object CreateScriptingObject(IntPtr unmanagedPtr, IntPtr idPtr) { object obj = RuntimeHelpers.GetUninitializedObject(wrappedType); diff --git a/Source/Engine/Scripting/ManagedCLR/MUtils.cpp b/Source/Engine/Scripting/ManagedCLR/MUtils.cpp index fd0596ef5..69fb5aa08 100644 --- a/Source/Engine/Scripting/ManagedCLR/MUtils.cpp +++ b/Source/Engine/Scripting/ManagedCLR/MUtils.cpp @@ -414,8 +414,9 @@ Variant MUtils::UnboxVariant(MObject* value) // Array of Enums for (int32 i = 0; i < array.Count(); i++) { - array[i].SetType(VariantType(VariantType::Enum, elementTypename)); - Platform::MemoryCopy(&array[i].AsUint64, (byte*)ptr + elementSize * i, elementSize); + auto& a = array.Get()[i]; + a.SetType(VariantType(VariantType::Enum, elementTypename)); + Platform::MemoryCopy(&a.AsUint64, (byte*)ptr + elementSize * i, elementSize); } } else if (elementClass->IsValueType()) diff --git a/Source/Engine/Scripting/Runtime/DotNet.cpp b/Source/Engine/Scripting/Runtime/DotNet.cpp index 97065487c..f69e0c060 100644 --- a/Source/Engine/Scripting/Runtime/DotNet.cpp +++ b/Source/Engine/Scripting/Runtime/DotNet.cpp @@ -710,7 +710,7 @@ void MCore::ScriptingObject::SetInternalValues(MClass* klass, MObject* object, v #if PLATFORM_DESKTOP && !USE_MONO_AOT static void* ScriptingObjectSetInternalValuesPtr = GetStaticMethodPointer(TEXT("ScriptingObjectSetInternalValues")); CallStaticMethod(ScriptingObjectSetInternalValuesPtr, object, unmanagedPtr, id); -#elif !USE_EDITOR +#elif !USE_EDITOR && PLATFORM_SWITCH // TODO: test this on other AOT platforms (Android with Mono JIT doesn't work) static MField* monoUnmanagedPtrField = ::ScriptingObject::GetStaticClass()->GetField("__unmanagedPtr"); static MField* monoIdField = ::ScriptingObject::GetStaticClass()->GetField("__internalId"); if (monoUnmanagedPtrField) diff --git a/Source/Engine/Scripting/ScriptingObject.cpp b/Source/Engine/Scripting/ScriptingObject.cpp index eb9afdeae..35bd16eb6 100644 --- a/Source/Engine/Scripting/ScriptingObject.cpp +++ b/Source/Engine/Scripting/ScriptingObject.cpp @@ -249,7 +249,7 @@ ScriptingObject* ScriptingObject::ToNative(MObject* obj) #if USE_CSHARP if (obj) { -#if USE_MONO || USE_MONO_AOT +#if USE_MONO || USE_MONO_AOT || DOTNET_HOST_MONO const auto ptrField = MCore::Object::GetClass(obj)->GetField(ScriptingObject_unmanagedPtr); CHECK_RETURN(ptrField, nullptr); ptrField->GetValue(obj, &ptr); diff --git a/Source/Engine/Video/Android/VideoBackendAndroid.cpp b/Source/Engine/Video/Android/VideoBackendAndroid.cpp index 3358c46a2..145ce8aab 100644 --- a/Source/Engine/Video/Android/VideoBackendAndroid.cpp +++ b/Source/Engine/Video/Android/VideoBackendAndroid.cpp @@ -353,7 +353,7 @@ bool VideoBackendAndroid::Player_Create(const VideoBackendPlayerInfo& info, Vide { // File (AAsset* or Unix handle) #if VIDEO_API_ANDROID_DEBUG - LOG(Info, "[VideoBackendAndroid] Loading local file"); + LOG(Info, "[VideoBackendAndroid] Loading local file '{}'", info.Url); #endif auto* mediaSource = AMediaDataSource_new(); AMediaDataSource_setUserdata(mediaSource, fileStream); @@ -366,7 +366,7 @@ bool VideoBackendAndroid::Player_Create(const VideoBackendPlayerInfo& info, Vide { // Url #if VIDEO_API_ANDROID_DEBUG - LOG(Info, "[VideoBackendAndroid] Loading url"); + LOG(Info, "[VideoBackendAndroid] Loading url '{}'", info.Url); #endif const StringAsANSI<> url(info.Url.Get(), info.Url.Length()); status = AMediaExtractor_setDataSource(playerAndroid.Extractor, url.Get()); diff --git a/Source/ThirdParty/nethost/nethost.Build.cs b/Source/ThirdParty/nethost/nethost.Build.cs index 97f0d80bd..bac529778 100644 --- a/Source/ThirdParty/nethost/nethost.Build.cs +++ b/Source/ThirdParty/nethost/nethost.Build.cs @@ -112,6 +112,7 @@ public class nethost : ThirdPartyModule { // Use CoreCLR for runtime hosting options.PublicDefinitions.Add("DOTNET_HOST_CORECLR"); + options.ScriptingAPI.Defines.Add("DOTNET_HOST_CORECLR"); options.PublicIncludePaths.Add(hostRuntime.Path); break; } @@ -119,6 +120,7 @@ public class nethost : ThirdPartyModule { // Use Mono for runtime hosting options.PublicDefinitions.Add("DOTNET_HOST_MONO"); + options.ScriptingAPI.Defines.Add("DOTNET_HOST_MONO"); options.PublicIncludePaths.Add(Path.Combine(hostRuntime.Path, "include", "mono-2.0")); break; }