Fix crashes of scripting backend on Android

This commit is contained in:
Wojtek Figat
2025-09-09 15:25:24 +02:00
parent 14a69a11df
commit 64e127a47a
7 changed files with 44 additions and 38 deletions

View File

@@ -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<FieldHolder>(fieldHandle.Target);
fieldRef = IntPtr.Zero;
Debug.LogError("Not supported FieldGetValueReference");

View File

@@ -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<int>((typeof(FlaxEngine.Object).GetField("__unmanagedPtr", BindingFlags.Instance | BindingFlags.NonPublic).FieldHandle.Value + 4 + IntPtr.Size).ToPointer()) & 0xFFFFFF);
private static int internalIdFieldOffset = IntPtr.Size + (Unsafe.Read<int>((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<int>((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
/// <summary>
/// Helper utility to set field of the referenced value via reflection.
/// </summary>
@@ -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<IntPtr>(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<IntPtr>(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<T, TField>(fieldOffset, ref fieldOwner);
#endif
MarshalHelper<TField>.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<T, TField>(fieldOffset, ref fieldOwner);
#endif
MarshalHelper<TField>.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<T, TField[]>(fieldOffset, ref fieldOwner);
#endif
MarshalHelper<TField[]>.ToManaged(ref fieldValue, Unsafe.Read<IntPtr>(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<T, TField[]>(fieldOffset, ref fieldOwner);
#endif
MarshalHelper<TField[]>.ToManaged(ref fieldValue, Unsafe.Read<IntPtr>(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<T, TField>(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<T, TField>(fieldOffset, ref fieldOwner);
#endif
MarshalHelper<TField>.ToManaged(ref fieldValue, Unsafe.Read<IntPtr>(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<T, TField>(fieldOffset, ref fieldOwner);
#endif
MarshalHelper<TField>.ToManaged(ref fieldValue, Unsafe.Read<IntPtr>(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<T, TField[]>(fieldOffset, ref fieldOwner);
#endif
MarshalHelper<TField[]>.ToManaged(ref fieldValue, Unsafe.Read<IntPtr>(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<T, TField[]>(fieldOffset, ref fieldOwner);
#endif
MarshalHelper<TField[]>.ToManaged(ref fieldValue, Unsafe.Read<IntPtr>(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<T, TField>(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<T, TField>(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);

View File

@@ -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())

View File

@@ -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<void, MObject*, void*, const Guid*>(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)

View File

@@ -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);

View File

@@ -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());

View File

@@ -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;
}