From 609217a3bbada219e4029b3183d22b06e371f4e0 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 12 Apr 2023 15:47:03 +0200 Subject: [PATCH] Fix using `Nullable` in C# properties Fixes #935 --- Source/Engine/Engine/NativeInterop.cs | 63 +++++++++++++++++++-------- 1 file changed, 45 insertions(+), 18 deletions(-) diff --git a/Source/Engine/Engine/NativeInterop.cs b/Source/Engine/Engine/NativeInterop.cs index b9dd3ac56..31d6bea68 100644 --- a/Source/Engine/Engine/NativeInterop.cs +++ b/Source/Engine/Engine/NativeInterop.cs @@ -371,40 +371,50 @@ namespace FlaxEngine.Interop { toManagedFieldMarshallers = new MarshalFieldTypedDelegate[marshallableFields.Length]; toNativeFieldMarshallers = new MarshalFieldTypedDelegate[marshallableFields.Length]; + BindingFlags bindingFlags = BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public; for (int i = 0; i < marshallableFields.Length; i++) { FieldInfo field = marshallableFields[i]; + Type fieldType = field.FieldType; MethodInfo toManagedFieldMethod; MethodInfo toNativeFieldMethod; - if (field.FieldType.IsPointer) + if (fieldType.IsPointer) { - toManagedFieldMethod = typeof(MarshalHelper<>).MakeGenericType(type).GetMethod(nameof(MarshalHelper.ToManagedFieldPointer), BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public); - toNativeFieldMethod = typeof(MarshalHelper<>).MakeGenericType(type).GetMethod(nameof(MarshalHelper.ToNativeFieldPointer), BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public); + toManagedFieldMethod = typeof(MarshalHelper<>).MakeGenericType(type).GetMethod(nameof(MarshalHelper.ToManagedFieldPointer), bindingFlags); + toNativeFieldMethod = typeof(MarshalHelper<>).MakeGenericType(type).GetMethod(nameof(MarshalHelper.ToNativeFieldPointer), bindingFlags); } - else if (field.FieldType.IsValueType) + else if (fieldType.IsValueType) { - toManagedFieldMethod = typeof(MarshalHelper<>.ValueTypeField<>).MakeGenericType(type, field.FieldType).GetMethod(nameof(MarshalHelper.ValueTypeField.ToManagedField), BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public); - toNativeFieldMethod = typeof(MarshalHelper<>.ValueTypeField<>).MakeGenericType(type, field.FieldType).GetMethod(nameof(MarshalHelper.ValueTypeField.ToNativeField), BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public); - } - else if (field.FieldType.IsArray) - { - Type arrayElementType = field.FieldType.GetElementType(); - if (arrayElementType.IsValueType) + if (Nullable.GetUnderlyingType(fieldType) != null) { - toManagedFieldMethod = typeof(MarshalHelper<>.ValueTypeField<>).MakeGenericType(type, arrayElementType).GetMethod(nameof(MarshalHelper.ValueTypeField.ToManagedFieldArray), BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public); - toNativeFieldMethod = typeof(MarshalHelper<>.ReferenceTypeField<>).MakeGenericType(type, field.FieldType).GetMethod(nameof(MarshalHelper.ReferenceTypeField.ToNativeField), BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public); + toManagedFieldMethod = typeof(MarshalHelper<>.NullableValueTypeField<>).MakeGenericType(type, fieldType).GetMethod(nameof(MarshalHelper.NullableValueTypeField.ToManagedField), bindingFlags); + toNativeFieldMethod = typeof(MarshalHelper<>.NullableValueTypeField<>).MakeGenericType(type, fieldType).GetMethod(nameof(MarshalHelper.NullableValueTypeField.ToNativeField), bindingFlags); } else { - toManagedFieldMethod = typeof(MarshalHelper<>.ReferenceTypeField<>).MakeGenericType(type, field.FieldType).GetMethod(nameof(MarshalHelper.ReferenceTypeField.ToManagedField), BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public); - toNativeFieldMethod = typeof(MarshalHelper<>.ReferenceTypeField<>).MakeGenericType(type, field.FieldType).GetMethod(nameof(MarshalHelper.ReferenceTypeField.ToNativeField), BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public); + toManagedFieldMethod = typeof(MarshalHelper<>.ValueTypeField<>).MakeGenericType(type, fieldType).GetMethod(nameof(MarshalHelper.ValueTypeField.ToManagedField), bindingFlags); + toNativeFieldMethod = typeof(MarshalHelper<>.ValueTypeField<>).MakeGenericType(type, fieldType).GetMethod(nameof(MarshalHelper.ValueTypeField.ToNativeField), bindingFlags); + } + } + else if (fieldType.IsArray) + { + Type arrayElementType = fieldType.GetElementType(); + if (arrayElementType.IsValueType) + { + toManagedFieldMethod = typeof(MarshalHelper<>.ValueTypeField<>).MakeGenericType(type, arrayElementType).GetMethod(nameof(MarshalHelper.ValueTypeField.ToManagedFieldArray), bindingFlags); + toNativeFieldMethod = typeof(MarshalHelper<>.ReferenceTypeField<>).MakeGenericType(type, fieldType).GetMethod(nameof(MarshalHelper.ReferenceTypeField.ToNativeField), bindingFlags); + } + else + { + toManagedFieldMethod = typeof(MarshalHelper<>.ReferenceTypeField<>).MakeGenericType(type, fieldType).GetMethod(nameof(MarshalHelper.ReferenceTypeField.ToManagedField), bindingFlags); + toNativeFieldMethod = typeof(MarshalHelper<>.ReferenceTypeField<>).MakeGenericType(type, fieldType).GetMethod(nameof(MarshalHelper.ReferenceTypeField.ToNativeField), bindingFlags); } } else { - toManagedFieldMethod = typeof(MarshalHelper<>.ReferenceTypeField<>).MakeGenericType(type, field.FieldType).GetMethod(nameof(MarshalHelper.ReferenceTypeField.ToManagedField), BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public); - toNativeFieldMethod = typeof(MarshalHelper<>.ReferenceTypeField<>).MakeGenericType(type, field.FieldType).GetMethod(nameof(MarshalHelper.ReferenceTypeField.ToNativeField), BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public); + toManagedFieldMethod = typeof(MarshalHelper<>.ReferenceTypeField<>).MakeGenericType(type, fieldType).GetMethod(nameof(MarshalHelper.ReferenceTypeField.ToManagedField), bindingFlags); + toNativeFieldMethod = typeof(MarshalHelper<>.ReferenceTypeField<>).MakeGenericType(type, fieldType).GetMethod(nameof(MarshalHelper.ReferenceTypeField.ToNativeField), bindingFlags); } toManagedFieldMarshallers[i] = toManagedFieldMethod.CreateDelegate(); toNativeFieldMarshallers[i] = toNativeFieldMethod.CreateDelegate(); @@ -583,6 +593,23 @@ namespace FlaxEngine.Interop } } + private static class NullableValueTypeField + { + static NullableValueTypeField() + { + } + + internal static void ToManagedField(FieldInfo field, ref T fieldOwner, IntPtr fieldPtr, out int fieldOffset) + { + fieldOffset = 0; + } + + internal static void ToNativeField(FieldInfo field, ref T fieldOwner, IntPtr fieldPtr, out int fieldOffset) + { + fieldOffset = 0; + } + } + private static class ReferenceTypeField where TField : class { internal static void ToManagedField(FieldInfo field, ref T fieldOwner, IntPtr fieldPtr, out int fieldOffset) @@ -825,7 +852,7 @@ namespace FlaxEngine.Interop else genericParamTypes.Add(type); } - + string invokerTypeName = $"{typeof(Invoker).FullName}+Invoker{(method.IsStatic ? "Static" : "")}{(returnType != typeof(void) ? "Ret" : "NoRet")}{parameterTypes.Length}{(genericParamTypes.Count > 0 ? "`" + genericParamTypes.Count : "")}"; Type invokerType = Type.GetType(invokerTypeName); if (invokerType != null)