Fix scripting interop in AOT mode to use reflection-based field access

This commit is contained in:
Wojtek Figat
2023-10-11 15:18:28 +02:00
parent b92345c3ef
commit af468ee6ae
5 changed files with 173 additions and 57 deletions

View File

@@ -857,36 +857,25 @@ namespace FlaxEngine.Interop
}
[UnmanagedCallersOnly]
internal static void FieldGetValueReference(ManagedHandle fieldOwnerHandle, ManagedHandle fieldHandle, IntPtr valuePtr)
internal static void FieldGetValueReference(ManagedHandle fieldOwnerHandle, ManagedHandle fieldHandle, int fieldOffset, IntPtr valuePtr)
{
object fieldOwner = fieldOwnerHandle.Target;
IntPtr fieldRef;
#if USE_AOT
FieldHolder field = Unsafe.As<FieldHolder>(fieldHandle.Target);
fieldRef = IntPtr.Zero;
Debug.LogError("Not supported FieldGetValueReference");
#else
if (fieldOwner.GetType().IsValueType)
{
ref IntPtr fieldRef = ref FieldHelper.GetValueTypeFieldReference<object, IntPtr>(field.fieldOffset, ref fieldOwner);
Unsafe.Write<IntPtr>(valuePtr.ToPointer(), fieldRef);
fieldRef = FieldHelper.GetValueTypeFieldReference<object, IntPtr>(fieldOffset, ref fieldOwner);
}
else
{
ref IntPtr fieldRef = ref FieldHelper.GetReferenceTypeFieldReference<object, IntPtr>(field.fieldOffset, ref fieldOwner);
Unsafe.Write<IntPtr>(valuePtr.ToPointer(), fieldRef);
}
}
[UnmanagedCallersOnly]
internal static void FieldGetValueReferenceWithOffset(ManagedHandle fieldOwnerHandle, int fieldOffset, IntPtr valuePtr)
{
object fieldOwner = fieldOwnerHandle.Target;
if (fieldOwner.GetType().IsValueType)
{
ref IntPtr fieldRef = ref FieldHelper.GetValueTypeFieldReference<object, IntPtr>(fieldOffset, ref fieldOwner);
Unsafe.Write<IntPtr>(valuePtr.ToPointer(), fieldRef);
}
else
{
ref IntPtr fieldRef = ref FieldHelper.GetReferenceTypeFieldReference<object, IntPtr>(fieldOffset, ref fieldOwner);
Unsafe.Write<IntPtr>(valuePtr.ToPointer(), fieldRef);
fieldRef = FieldHelper.GetReferenceTypeFieldReference<object, IntPtr>(fieldOffset, ref fieldOwner);
}
#endif
Unsafe.Write<IntPtr>(valuePtr.ToPointer(), fieldRef);
}
[UnmanagedCallersOnly]

View File

@@ -119,6 +119,7 @@ namespace FlaxEngine.Interop
{
}
#if !USE_AOT
// 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);
@@ -150,6 +151,7 @@ namespace FlaxEngine.Interop
object obj = typeHolder.CreateScriptingObject(unmanagedPtr, idPtr);
return ManagedHandle.Alloc(obj);
}
#endif
internal static void* NativeAlloc(int byteCount)
{
@@ -439,6 +441,23 @@ namespace FlaxEngine.Interop
return fieldOffset;
}
#if USE_AOT
/// <summary>
/// Helper utility to set field of the referenced value via reflection.
/// </summary>
internal static void SetReferenceTypeField<T>(FieldInfo field, ref T fieldOwner, object fieldValue)
{
if (typeof(T).IsValueType)
{
// Value types need setting via boxed object to properly propagate value
object fieldOwnerBoxed = fieldOwner;
field.SetValue(fieldOwnerBoxed, fieldValue);
fieldOwner = (T)fieldOwnerBoxed;
}
else
field.SetValue(fieldOwner, fieldValue);
}
#else
/// <summary>
/// Returns a reference to the value of the field.
/// </summary>
@@ -465,6 +484,7 @@ namespace FlaxEngine.Interop
byte* fieldPtr = (byte*)Unsafe.As<T, IntPtr>(ref fieldOwner) + fieldOffset;
return ref Unsafe.AsRef<TField>(fieldPtr);
}
#endif
}
/// <summary>
@@ -738,29 +758,49 @@ namespace FlaxEngine.Interop
private static void ToManagedFieldPointerValueType(FieldInfo field, int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : struct
{
#if USE_AOT
IntPtr fieldValue = Unsafe.Read<IntPtr>(nativeFieldPtr.ToPointer());
FieldHelper.SetReferenceTypeField(field, ref fieldOwner, fieldValue);
#else
ref IntPtr fieldValueRef = ref FieldHelper.GetValueTypeFieldReference<T, IntPtr>(fieldOffset, ref fieldOwner);
fieldValueRef = Unsafe.Read<IntPtr>(nativeFieldPtr.ToPointer());
#endif
fieldSize = IntPtr.Size;
}
private static void ToManagedFieldPointerReferenceType(FieldInfo field, int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : class
{
#if USE_AOT
IntPtr fieldValue = Unsafe.Read<IntPtr>(nativeFieldPtr.ToPointer());
FieldHelper.SetReferenceTypeField(field, ref fieldOwner, fieldValue);
#else
ref IntPtr fieldValueRef = ref FieldHelper.GetReferenceTypeFieldReference<T, IntPtr>(fieldOffset, ref fieldOwner);
fieldValueRef = Unsafe.Read<IntPtr>(nativeFieldPtr.ToPointer());
#endif
fieldSize = IntPtr.Size;
}
private static void ToNativeFieldPointerValueType(FieldInfo field, int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : struct
{
ref IntPtr fieldValueRef = ref FieldHelper.GetValueTypeFieldReference<T, IntPtr>(fieldOffset, ref fieldOwner);
Unsafe.Write<IntPtr>(nativeFieldPtr.ToPointer(), fieldValueRef);
#if USE_AOT
object boxed = field.GetValue(fieldOwner);
IntPtr fieldValue = new IntPtr(Pointer.Unbox(boxed));
#else
IntPtr fieldValue = FieldHelper.GetValueTypeFieldReference<T, IntPtr>(fieldOffset, ref fieldOwner);
#endif
Unsafe.Write<IntPtr>(nativeFieldPtr.ToPointer(), fieldValue);
fieldSize = IntPtr.Size;
}
private static void ToNativeFieldPointerReferenceType(FieldInfo field, int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : class
{
ref IntPtr fieldValueRef = ref FieldHelper.GetReferenceTypeFieldReference<T, IntPtr>(fieldOffset, ref fieldOwner);
Unsafe.Write<IntPtr>(nativeFieldPtr.ToPointer(), fieldValueRef);
#if USE_AOT
object boxed = field.GetValue(fieldOwner);
IntPtr fieldValue = new IntPtr(Pointer.Unbox(boxed));
#else
IntPtr fieldValue = FieldHelper.GetReferenceTypeFieldReference<T, IntPtr>(fieldOffset, ref fieldOwner);
#endif
Unsafe.Write<IntPtr>(nativeFieldPtr.ToPointer(), fieldValue);
fieldSize = IntPtr.Size;
}
@@ -802,8 +842,15 @@ namespace FlaxEngine.Interop
fieldSize += (nativeFieldPtr - fieldStartPtr).ToInt32();
}
ref TField fieldValueRef = ref FieldHelper.GetValueTypeFieldReference<T, TField>(fieldOffset, ref fieldOwner);
MarshalHelper<TField>.ToManaged(ref fieldValueRef, nativeFieldPtr, false);
#if USE_AOT
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
FieldHelper.SetReferenceTypeField(field, ref fieldOwner, fieldValue);
#endif
}
internal static void ToManagedFieldReferenceType(FieldInfo field, int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : class
@@ -816,8 +863,15 @@ namespace FlaxEngine.Interop
fieldSize += (nativeFieldPtr - fieldStartPtr).ToInt32();
}
ref TField fieldValueRef = ref FieldHelper.GetReferenceTypeFieldReference<T, TField>(fieldOffset, ref fieldOwner);
MarshalHelper<TField>.ToManaged(ref fieldValueRef, nativeFieldPtr, false);
#if USE_AOT
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
FieldHelper.SetReferenceTypeField(field, ref fieldOwner, fieldValue);
#endif
}
internal static void ToManagedFieldArrayValueType(FieldInfo field, int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : struct
@@ -828,8 +882,15 @@ namespace FlaxEngine.Interop
nativeFieldPtr = EnsureAlignment(nativeFieldPtr, IntPtr.Size);
fieldSize += (nativeFieldPtr - fieldStartPtr).ToInt32();
ref TField[] fieldValueRef = ref FieldHelper.GetValueTypeFieldReference<T, TField[]>(fieldOffset, ref fieldOwner);
MarshalHelper<TField[]>.ToManaged(ref fieldValueRef, Unsafe.Read<IntPtr>(nativeFieldPtr.ToPointer()), false);
#if USE_AOT
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
FieldHelper.SetReferenceTypeField(field, ref fieldOwner, fieldValue);
#endif
}
internal static void ToManagedFieldArrayReferenceType(FieldInfo field, int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : class
@@ -840,8 +901,15 @@ namespace FlaxEngine.Interop
nativeFieldPtr = EnsureAlignment(nativeFieldPtr, IntPtr.Size);
fieldSize += (nativeFieldPtr - fieldStartPtr).ToInt32();
ref TField[] fieldValueRef = ref FieldHelper.GetReferenceTypeFieldReference<T, TField[]>(fieldOffset, ref fieldOwner);
MarshalHelper<TField[]>.ToManaged(ref fieldValueRef, Unsafe.Read<IntPtr>(nativeFieldPtr.ToPointer()), false);
#if USE_AOT
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
FieldHelper.SetReferenceTypeField(field, ref fieldOwner, fieldValue);
#endif
}
internal static void ToNativeFieldValueType(FieldInfo field, int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : struct
@@ -855,11 +923,11 @@ namespace FlaxEngine.Interop
}
#if USE_AOT
TField fieldValueRef = (TField)field.GetValue(fieldOwner);
TField fieldValue = (TField)field.GetValue(fieldOwner);
#else
ref TField fieldValueRef = ref FieldHelper.GetValueTypeFieldReference<T, TField>(fieldOffset, ref fieldOwner);
ref TField fieldValue = ref FieldHelper.GetValueTypeFieldReference<T, TField>(fieldOffset, ref fieldOwner);
#endif
MarshalHelper<TField>.ToNative(ref fieldValueRef, nativeFieldPtr);
MarshalHelper<TField>.ToNative(ref fieldValue, nativeFieldPtr);
}
internal static void ToNativeFieldReferenceType(FieldInfo field, int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : class
@@ -873,11 +941,11 @@ namespace FlaxEngine.Interop
}
#if USE_AOT
TField fieldValueRef = (TField)field.GetValue(fieldOwner);
TField fieldValue = (TField)field.GetValue(fieldOwner);
#else
ref TField fieldValueRef = ref FieldHelper.GetReferenceTypeFieldReference<T, TField>(fieldOffset, ref fieldOwner);
ref TField fieldValue = ref FieldHelper.GetReferenceTypeFieldReference<T, TField>(fieldOffset, ref fieldOwner);
#endif
MarshalHelper<TField>.ToNative(ref fieldValueRef, nativeFieldPtr);
MarshalHelper<TField>.ToNative(ref fieldValue, nativeFieldPtr);
}
}
@@ -907,8 +975,15 @@ namespace FlaxEngine.Interop
nativeFieldPtr = EnsureAlignment(nativeFieldPtr, IntPtr.Size);
fieldSize += (nativeFieldPtr - fieldStartPtr).ToInt32();
ref TField fieldValueRef = ref FieldHelper.GetValueTypeFieldReference<T, TField>(fieldOffset, ref fieldOwner);
MarshalHelper<TField>.ToManaged(ref fieldValueRef, Unsafe.Read<IntPtr>(nativeFieldPtr.ToPointer()), false);
#if USE_AOT
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
FieldHelper.SetReferenceTypeField(field, ref fieldOwner, fieldValue);
#endif
}
internal static void ToManagedFieldReferenceType(FieldInfo field, int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : class
@@ -918,8 +993,15 @@ namespace FlaxEngine.Interop
nativeFieldPtr = EnsureAlignment(nativeFieldPtr, IntPtr.Size);
fieldSize += (nativeFieldPtr - fieldStartPtr).ToInt32();
ref TField fieldValueRef = ref FieldHelper.GetReferenceTypeFieldReference<T, TField>(fieldOffset, ref fieldOwner);
MarshalHelper<TField>.ToManaged(ref fieldValueRef, Unsafe.Read<IntPtr>(nativeFieldPtr.ToPointer()), false);
#if USE_AOT
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
FieldHelper.SetReferenceTypeField(field, ref fieldOwner, fieldValue);
#endif
}
internal static void ToManagedFieldArrayValueType(FieldInfo field, int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : struct
@@ -929,8 +1011,15 @@ namespace FlaxEngine.Interop
nativeFieldPtr = EnsureAlignment(nativeFieldPtr, IntPtr.Size);
fieldSize += (nativeFieldPtr - fieldStartPtr).ToInt32();
ref TField[] fieldValueRef = ref FieldHelper.GetValueTypeFieldReference<T, TField[]>(fieldOffset, ref fieldOwner);
MarshalHelper<TField[]>.ToManaged(ref fieldValueRef, Unsafe.Read<IntPtr>(nativeFieldPtr.ToPointer()), false);
#if USE_AOT
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
FieldHelper.SetReferenceTypeField(field, ref fieldOwner, fieldValue);
#endif
}
internal static void ToManagedFieldArrayReferenceType(FieldInfo field, int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : class
@@ -940,8 +1029,15 @@ namespace FlaxEngine.Interop
nativeFieldPtr = EnsureAlignment(nativeFieldPtr, IntPtr.Size);
fieldSize += (nativeFieldPtr - fieldStartPtr).ToInt32();
ref TField[] fieldValueRef = ref FieldHelper.GetReferenceTypeFieldReference<T, TField[]>(fieldOffset, ref fieldOwner);
MarshalHelper<TField[]>.ToManaged(ref fieldValueRef, Unsafe.Read<IntPtr>(nativeFieldPtr.ToPointer()), false);
#if USE_AOT
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
FieldHelper.SetReferenceTypeField(field, ref fieldOwner, fieldValue);
#endif
}
internal static void ToNativeFieldValueType(FieldInfo field, int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : struct
@@ -951,8 +1047,12 @@ namespace FlaxEngine.Interop
nativeFieldPtr = EnsureAlignment(nativeFieldPtr, IntPtr.Size);
fieldSize += (nativeFieldPtr - fieldStartPtr).ToInt32();
ref TField fieldValueRef = ref FieldHelper.GetValueTypeFieldReference<T, TField>(fieldOffset, ref fieldOwner);
MarshalHelper<TField>.ToNative(ref fieldValueRef, nativeFieldPtr);
#if USE_AOT
TField fieldValue = (TField)field.GetValue(fieldOwner);
#else
ref TField fieldValue = ref FieldHelper.GetValueTypeFieldReference<T, TField>(fieldOffset, ref fieldOwner);
#endif
MarshalHelper<TField>.ToNative(ref fieldValue, nativeFieldPtr);
}
internal static void ToNativeFieldReferenceType(FieldInfo field, int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : class
@@ -962,8 +1062,12 @@ namespace FlaxEngine.Interop
nativeFieldPtr = EnsureAlignment(nativeFieldPtr, IntPtr.Size);
fieldSize += (nativeFieldPtr - fieldStartPtr).ToInt32();
ref TField fieldValueRef = ref FieldHelper.GetReferenceTypeFieldReference<T, TField>(fieldOffset, ref fieldOwner);
MarshalHelper<TField>.ToNative(ref fieldValueRef, nativeFieldPtr);
#if USE_AOT
TField fieldValue = (TField)field.GetValue(fieldOwner);
#else
ref TField fieldValue = ref FieldHelper.GetReferenceTypeFieldReference<T, TField>(fieldOffset, ref fieldOwner);
#endif
MarshalHelper<TField>.ToNative(ref fieldValue, nativeFieldPtr);
}
}
}
@@ -1047,7 +1151,7 @@ namespace FlaxEngine.Interop
var fields = MarshalHelper<T>.marshallableFields;
var offsets = MarshalHelper<T>.marshallableFieldOffsets;
var marshallers = MarshalHelper<T>.toNativeFieldMarshallers;
for (int i = 0; i < MarshalHelper<T>.marshallableFields.Length; i++)
for (int i = 0; i < fields.Length; i++)
{
marshallers[i](fields[i], offsets[i], ref managedValue, nativePtr, out int fieldSize);
nativePtr += fieldSize;
@@ -1309,11 +1413,13 @@ namespace FlaxEngine.Interop
return RuntimeHelpers.GetUninitializedObject(wrappedType);
}
#if !USE_AOT
internal object CreateScriptingObject(IntPtr unmanagedPtr, IntPtr idPtr)
{
object obj = RuntimeHelpers.GetUninitializedObject(wrappedType);
if (obj is Object)
{
// TODO: use UnsafeAccessorAttribute on .NET 8 and use this path on all platforms (including non-Desktop, see MCore::ScriptingObject::CreateScriptingObject)
{
ref IntPtr fieldRef = ref FieldHelper.GetReferenceTypeFieldReference<IntPtr>(unmanagedPtrFieldOffset, ref obj);
fieldRef = unmanagedPtr;
@@ -1334,6 +1440,7 @@ namespace FlaxEngine.Interop
return obj;
}
#endif
public static implicit operator Type(TypeHolder holder) => holder?.type;
public bool Equals(TypeHolder other) => type == other.type;