From 7617f7dc16def0b7a06cffd3b2de097fc598bcc4 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sun, 21 Jan 2024 20:47:30 +0100 Subject: [PATCH 1/2] Make marshalling 'System.Type' AOT-safe --- src/WinRT.Runtime/FundamentalMarshalers.cs | 182 +++++++++++++++++++++ src/WinRT.Runtime/Marshalers.cs | 43 ++++- src/WinRT.Runtime/Projections/Type.cs | 2 +- 3 files changed, 218 insertions(+), 9 deletions(-) diff --git a/src/WinRT.Runtime/FundamentalMarshalers.cs b/src/WinRT.Runtime/FundamentalMarshalers.cs index a6290a5c4..84dcdba45 100644 --- a/src/WinRT.Runtime/FundamentalMarshalers.cs +++ b/src/WinRT.Runtime/FundamentalMarshalers.cs @@ -2,6 +2,8 @@ // Licensed under the MIT License. using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace ABI.System { @@ -39,6 +41,8 @@ internal static class NonBlittableMarshallingStubs public static unsafe void DateTimeOffset_CopyAbi(object value, IntPtr dest) => DateTimeOffset.CopyAbi((DateTimeOffset.Marshaler)value, dest); public static object DateTimeOffset_FromManaged(global::System.DateTimeOffset value) => DateTimeOffset.FromManaged(value); + public static object Type_CreateMarshalerArray(global::System.Type[] value) => Type.CreateMarshalerArray(value); + private static void NoOp(object obj) { } @@ -67,5 +71,183 @@ internal struct Char public static void DisposeMarshaler(char m) { } public static void DisposeAbi(ushort abi) { } } + + // This partial declaration contains additional marshalling methods for the ABI type for System.Type. + // These are hardcoded for the ABI.System.Type type, which makes them AOT-safe. Previously, these were + // relying on AOT-unfriendly code in MarshalNonBlittable. Because this is a well known type, we can + // just hardcode explicit marshalling methods for it instead to make using this type fully AOT-safe. + partial struct Type + { + internal struct MarshalerArray + { + public void Dispose() + { + if (_marshalers != null) + { + foreach (var marshaler in _marshalers) + { + DisposeMarshaler((Marshaler)marshaler); + } + } + if (_array != IntPtr.Zero) + { + Marshal.FreeCoTaskMem(_array); + } + } + + public IntPtr _array; + public object[] _marshalers; + } + + internal static unsafe MarshalerArray CreateMarshalerArray(global::System.Type[] array) + { + MarshalerArray m = new MarshalerArray(); + if (array is null) + { + return m; + } + bool success = false; + try + { + int length = array.Length; + var abi_element_size = sizeof(Type); + var byte_length = length * abi_element_size; + m._array = Marshal.AllocCoTaskMem(byte_length); + m._marshalers = new object[length]; + var element = (byte*)m._array.ToPointer(); + for (int i = 0; i < length; i++) + { + m._marshalers[i] = CreateMarshaler(array[i]); + CopyAbi((Marshaler)m._marshalers[i], (IntPtr)element); + element += abi_element_size; + } + success = true; + return m; + } + finally + { + if (!success) + { + m.Dispose(); + } + } + } + + internal static (int length, IntPtr data) GetAbiArray(object box) + { + var m = (MarshalerArray)box; + return (m._marshalers?.Length ?? 0, m._array); + } + + internal static unsafe global::System.Type[] FromAbiArray(object box) + { + if (box is null) + { + return null; + } + var abi = ((int length, IntPtr data))box; + if (abi.data == IntPtr.Zero) + { + return null; + } + var array = new global::System.Type[abi.length]; + var data = (byte*)abi.data.ToPointer(); + var abi_element_size = sizeof(Type); + for (int i = 0; i < abi.length; i++) + { + var abi_element = Unsafe.ReadUnaligned(data); + array[i] = FromAbi(abi_element); + data += abi_element_size; + } + return array; + } + + internal static unsafe (int length, IntPtr data) FromManagedArray(global::System.Type[] array) + { + if (array is null) + { + return (0, IntPtr.Zero); + } + IntPtr data = IntPtr.Zero; + int i = 0; + bool success = false; + try + { + int length = array.Length; + var abi_element_size = sizeof(Type); + var byte_length = length * abi_element_size; + data = Marshal.AllocCoTaskMem(byte_length); + var bytes = (byte*)data.ToPointer(); + for (i = 0; i < length; i++) + { + CopyManaged(array[i], (IntPtr)bytes); + bytes += abi_element_size; + } + success = true; + return (i, data); + } + finally + { + if (!success) + { + DisposeAbiArray((i, data)); + } + } + } + + internal static unsafe void CopyManagedArray(global::System.Type[] array, IntPtr data) + { + if (array is null) + { + return; + } + DisposeAbiArrayElements((array.Length, data)); + int i = 0; + bool success = false; + try + { + int length = array.Length; + var abi_element_size = sizeof(Type); + var byte_length = length * abi_element_size; + var bytes = (byte*)data.ToPointer(); + for (i = 0; i < length; i++) + { + CopyManaged(array[i], (IntPtr)bytes); + bytes += abi_element_size; + } + success = true; + } + finally + { + if (!success) + { + DisposeAbiArrayElements((i, data)); + } + } + } + + internal static void DisposeMarshalerArray(object box) => ((MarshalerArray)box).Dispose(); + + internal static unsafe void DisposeAbiArray(object box) + { + if (box == null) return; + var abi = ((int length, IntPtr data))box; + if (abi.data == IntPtr.Zero) return; + DisposeAbiArrayElements(abi); + Marshal.FreeCoTaskMem(abi.data); + } + + private static unsafe void DisposeAbiArrayElements((int length, IntPtr data) abi) + { + var data = (byte*)abi.data.ToPointer(); + var abi_element_size = sizeof(Type); + for (int i = 0; i < abi.length; i++) + { + var abi_element = Unsafe.ReadUnaligned(data); + DisposeAbi(abi_element); + data += abi_element_size; + } + } + } } diff --git a/src/WinRT.Runtime/Marshalers.cs b/src/WinRT.Runtime/Marshalers.cs index 3954d4fb6..1e47cab77 100644 --- a/src/WinRT.Runtime/Marshalers.cs +++ b/src/WinRT.Runtime/Marshalers.cs @@ -875,6 +875,12 @@ private static Type GetAbiType() return null; } + // Exclude Type, as it has dedicated marshalling code available from Marshaler + if (typeof(T) == typeof(Type)) + { + throw new NotSupportedException("Using 'System.Type' with MarshalNonBlittable isn't supported, use Marshaler instead."); + } + // Fallback path with the original logic return typeof(T).GetAbiType(); } @@ -900,6 +906,9 @@ public void Dispose() public object[] _marshalers; } +#if NET8_0_OR_GREATER + [RequiresDynamicCode("Marshalling code for the ABI type might not be available.")] +#endif public static new unsafe MarshalerArray CreateMarshalerArray(T[] array) { MarshalerArray m = new MarshalerArray(); @@ -940,6 +949,9 @@ public void Dispose() return (m._marshalers?.Length ?? 0, m._array); } +#if NET8_0_OR_GREATER + [RequiresDynamicCode("Marshalling code for the ABI type might not be available.")] +#endif public static new unsafe T[] FromAbiArray(object box) { if (box is null) @@ -963,6 +975,9 @@ public void Dispose() return array; } +#if NET8_0_OR_GREATER + [RequiresDynamicCode("Marshalling code for the ABI type might not be available.")] +#endif public static unsafe void CopyAbiArray(T[] array, object box) { var abi = ((int length, IntPtr data))box; @@ -980,6 +995,9 @@ public static unsafe void CopyAbiArray(T[] array, object box) } } +#if NET8_0_OR_GREATER + [RequiresDynamicCode("Marshalling code for the ABI type might not be available.")] +#endif public static new unsafe (int length, IntPtr data) FromManagedArray(T[] array) { if (array is null) @@ -1013,7 +1031,10 @@ public static unsafe void CopyAbiArray(T[] array, object box) } } - public static new unsafe void CopyManagedArray(T[] array, IntPtr data) +#if NET8_0_OR_GREATER + [RequiresDynamicCode("Marshalling code for the ABI type might not be available.")] +#endif + public static unsafe void CopyManagedArray(T[] array, IntPtr data) { if (array is null) { @@ -1046,6 +1067,9 @@ public static unsafe void CopyAbiArray(T[] array, object box) public static new void DisposeMarshalerArray(object box) => ((MarshalerArray)box).Dispose(); +#if NET8_0_OR_GREATER + [RequiresDynamicCode("Marshalling code for the ABI type might not be available.")] +#endif public static unsafe void DisposeAbiArrayElements((int length, IntPtr data) abi) { var data = (byte*)abi.data.ToPointer(); @@ -1058,6 +1082,9 @@ public static unsafe void DisposeAbiArrayElements((int length, IntPtr data) abi) } } +#if NET8_0_OR_GREATER + [RequiresDynamicCode("Marshalling code for the ABI type might not be available.")] +#endif public static new unsafe void DisposeAbiArray(object box) { if (box == null) return; @@ -1725,13 +1752,13 @@ static Marshaler() FromManaged = (T value) => ABI.System.Type.FromManaged((Type)(object)value); DisposeMarshaler = (object box) => ABI.System.Type.DisposeMarshaler((ABI.System.Type.Marshaler)box); DisposeAbi = (object box) => ABI.System.Type.DisposeAbi((ABI.System.Type)box); - CreateMarshalerArray = (T[] array) => MarshalNonBlittable.CreateMarshalerArray(array); - GetAbiArray = MarshalNonBlittable.GetAbiArray; - FromAbiArray = MarshalNonBlittable.FromAbiArray; - FromManagedArray = MarshalNonBlittable.FromManagedArray; - CopyManagedArray = MarshalNonBlittable.CopyManagedArray; - DisposeMarshalerArray = MarshalNonBlittable.DisposeMarshalerArray; - DisposeAbiArray = MarshalNonBlittable.DisposeAbiArray; + CreateMarshalerArray = (Func)(object)new Func(ABI.System.NonBlittableMarshallingStubs.Type_CreateMarshalerArray); + GetAbiArray = new Func(ABI.System.Type.GetAbiArray); + FromAbiArray = (Func)(object)new Func(ABI.System.Type.FromAbiArray); + FromManagedArray = (Func)(object)new Func(ABI.System.Type.FromManagedArray); + CopyManagedArray = (Action)(object)new Action(ABI.System.Type.CopyManagedArray); + DisposeMarshalerArray = new Action(ABI.System.Type.DisposeMarshalerArray); + DisposeAbiArray = new Action(ABI.System.Type.DisposeAbiArray); } else if (typeof(T).IsValueType) { diff --git a/src/WinRT.Runtime/Projections/Type.cs b/src/WinRT.Runtime/Projections/Type.cs index 80bcd751d..7c4d39f8d 100644 --- a/src/WinRT.Runtime/Projections/Type.cs +++ b/src/WinRT.Runtime/Projections/Type.cs @@ -23,7 +23,7 @@ internal enum TypeKind : int #else public #endif - struct Type + partial struct Type { private IntPtr Name; private TypeKind Kind; From 9f265c3c31e45bb981887cde877fd32c71fc1223 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Mon, 22 Jan 2024 21:11:23 +0100 Subject: [PATCH 2/2] Move marshalling methods for Type to partial file --- src/WinRT.Runtime/FundamentalMarshalers.cs | 178 ----------------- .../Projections/Type.ArrayMarshalling.cs | 187 ++++++++++++++++++ 2 files changed, 187 insertions(+), 178 deletions(-) create mode 100644 src/WinRT.Runtime/Projections/Type.ArrayMarshalling.cs diff --git a/src/WinRT.Runtime/FundamentalMarshalers.cs b/src/WinRT.Runtime/FundamentalMarshalers.cs index 84dcdba45..3f25ba79b 100644 --- a/src/WinRT.Runtime/FundamentalMarshalers.cs +++ b/src/WinRT.Runtime/FundamentalMarshalers.cs @@ -71,183 +71,5 @@ internal struct Char public static void DisposeMarshaler(char m) { } public static void DisposeAbi(ushort abi) { } } - - // This partial declaration contains additional marshalling methods for the ABI type for System.Type. - // These are hardcoded for the ABI.System.Type type, which makes them AOT-safe. Previously, these were - // relying on AOT-unfriendly code in MarshalNonBlittable. Because this is a well known type, we can - // just hardcode explicit marshalling methods for it instead to make using this type fully AOT-safe. - partial struct Type - { - internal struct MarshalerArray - { - public void Dispose() - { - if (_marshalers != null) - { - foreach (var marshaler in _marshalers) - { - DisposeMarshaler((Marshaler)marshaler); - } - } - if (_array != IntPtr.Zero) - { - Marshal.FreeCoTaskMem(_array); - } - } - - public IntPtr _array; - public object[] _marshalers; - } - - internal static unsafe MarshalerArray CreateMarshalerArray(global::System.Type[] array) - { - MarshalerArray m = new MarshalerArray(); - if (array is null) - { - return m; - } - bool success = false; - try - { - int length = array.Length; - var abi_element_size = sizeof(Type); - var byte_length = length * abi_element_size; - m._array = Marshal.AllocCoTaskMem(byte_length); - m._marshalers = new object[length]; - var element = (byte*)m._array.ToPointer(); - for (int i = 0; i < length; i++) - { - m._marshalers[i] = CreateMarshaler(array[i]); - CopyAbi((Marshaler)m._marshalers[i], (IntPtr)element); - element += abi_element_size; - } - success = true; - return m; - } - finally - { - if (!success) - { - m.Dispose(); - } - } - } - - internal static (int length, IntPtr data) GetAbiArray(object box) - { - var m = (MarshalerArray)box; - return (m._marshalers?.Length ?? 0, m._array); - } - - internal static unsafe global::System.Type[] FromAbiArray(object box) - { - if (box is null) - { - return null; - } - var abi = ((int length, IntPtr data))box; - if (abi.data == IntPtr.Zero) - { - return null; - } - var array = new global::System.Type[abi.length]; - var data = (byte*)abi.data.ToPointer(); - var abi_element_size = sizeof(Type); - for (int i = 0; i < abi.length; i++) - { - var abi_element = Unsafe.ReadUnaligned(data); - array[i] = FromAbi(abi_element); - data += abi_element_size; - } - return array; - } - - internal static unsafe (int length, IntPtr data) FromManagedArray(global::System.Type[] array) - { - if (array is null) - { - return (0, IntPtr.Zero); - } - IntPtr data = IntPtr.Zero; - int i = 0; - bool success = false; - try - { - int length = array.Length; - var abi_element_size = sizeof(Type); - var byte_length = length * abi_element_size; - data = Marshal.AllocCoTaskMem(byte_length); - var bytes = (byte*)data.ToPointer(); - for (i = 0; i < length; i++) - { - CopyManaged(array[i], (IntPtr)bytes); - bytes += abi_element_size; - } - success = true; - return (i, data); - } - finally - { - if (!success) - { - DisposeAbiArray((i, data)); - } - } - } - - internal static unsafe void CopyManagedArray(global::System.Type[] array, IntPtr data) - { - if (array is null) - { - return; - } - DisposeAbiArrayElements((array.Length, data)); - int i = 0; - bool success = false; - try - { - int length = array.Length; - var abi_element_size = sizeof(Type); - var byte_length = length * abi_element_size; - var bytes = (byte*)data.ToPointer(); - for (i = 0; i < length; i++) - { - CopyManaged(array[i], (IntPtr)bytes); - bytes += abi_element_size; - } - success = true; - } - finally - { - if (!success) - { - DisposeAbiArrayElements((i, data)); - } - } - } - - internal static void DisposeMarshalerArray(object box) => ((MarshalerArray)box).Dispose(); - - internal static unsafe void DisposeAbiArray(object box) - { - if (box == null) return; - var abi = ((int length, IntPtr data))box; - if (abi.data == IntPtr.Zero) return; - DisposeAbiArrayElements(abi); - Marshal.FreeCoTaskMem(abi.data); - } - - private static unsafe void DisposeAbiArrayElements((int length, IntPtr data) abi) - { - var data = (byte*)abi.data.ToPointer(); - var abi_element_size = sizeof(Type); - for (int i = 0; i < abi.length; i++) - { - var abi_element = Unsafe.ReadUnaligned(data); - DisposeAbi(abi_element); - data += abi_element_size; - } - } - } } diff --git a/src/WinRT.Runtime/Projections/Type.ArrayMarshalling.cs b/src/WinRT.Runtime/Projections/Type.ArrayMarshalling.cs new file mode 100644 index 000000000..1a981ee74 --- /dev/null +++ b/src/WinRT.Runtime/Projections/Type.ArrayMarshalling.cs @@ -0,0 +1,187 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace ABI.System +{ + // This partial declaration contains additional marshalling methods for the ABI type for System.Type. + // These are hardcoded for the ABI.System.Type type, which makes them AOT-safe. Previously, these were + // relying on AOT-unfriendly code in MarshalNonBlittable. Because this is a well known type, we can + // just hardcode explicit marshalling methods for it instead to make using this type fully AOT-safe. + partial struct Type + { + internal struct MarshalerArray + { + public void Dispose() + { + if (_marshalers != null) + { + foreach (var marshaler in _marshalers) + { + DisposeMarshaler((Marshaler)marshaler); + } + } + if (_array != IntPtr.Zero) + { + Marshal.FreeCoTaskMem(_array); + } + } + + public IntPtr _array; + public object[] _marshalers; + } + + internal static unsafe MarshalerArray CreateMarshalerArray(global::System.Type[] array) + { + MarshalerArray m = new MarshalerArray(); + if (array is null) + { + return m; + } + bool success = false; + try + { + int length = array.Length; + var abi_element_size = sizeof(Type); + var byte_length = length * abi_element_size; + m._array = Marshal.AllocCoTaskMem(byte_length); + m._marshalers = new object[length]; + var element = (byte*)m._array.ToPointer(); + for (int i = 0; i < length; i++) + { + m._marshalers[i] = CreateMarshaler(array[i]); + CopyAbi((Marshaler)m._marshalers[i], (IntPtr)element); + element += abi_element_size; + } + success = true; + return m; + } + finally + { + if (!success) + { + m.Dispose(); + } + } + } + + internal static (int length, IntPtr data) GetAbiArray(object box) + { + var m = (MarshalerArray)box; + return (m._marshalers?.Length ?? 0, m._array); + } + + internal static unsafe global::System.Type[] FromAbiArray(object box) + { + if (box is null) + { + return null; + } + var abi = ((int length, IntPtr data))box; + if (abi.data == IntPtr.Zero) + { + return null; + } + var array = new global::System.Type[abi.length]; + var data = (byte*)abi.data.ToPointer(); + var abi_element_size = sizeof(Type); + for (int i = 0; i < abi.length; i++) + { + var abi_element = Unsafe.ReadUnaligned(data); + array[i] = FromAbi(abi_element); + data += abi_element_size; + } + return array; + } + + internal static unsafe (int length, IntPtr data) FromManagedArray(global::System.Type[] array) + { + if (array is null) + { + return (0, IntPtr.Zero); + } + IntPtr data = IntPtr.Zero; + int i = 0; + bool success = false; + try + { + int length = array.Length; + var abi_element_size = sizeof(Type); + var byte_length = length * abi_element_size; + data = Marshal.AllocCoTaskMem(byte_length); + var bytes = (byte*)data.ToPointer(); + for (i = 0; i < length; i++) + { + CopyManaged(array[i], (IntPtr)bytes); + bytes += abi_element_size; + } + success = true; + return (i, data); + } + finally + { + if (!success) + { + DisposeAbiArray((i, data)); + } + } + } + + internal static unsafe void CopyManagedArray(global::System.Type[] array, IntPtr data) + { + if (array is null) + { + return; + } + DisposeAbiArrayElements((array.Length, data)); + int i = 0; + bool success = false; + try + { + int length = array.Length; + var abi_element_size = sizeof(Type); + var byte_length = length * abi_element_size; + var bytes = (byte*)data.ToPointer(); + for (i = 0; i < length; i++) + { + CopyManaged(array[i], (IntPtr)bytes); + bytes += abi_element_size; + } + success = true; + } + finally + { + if (!success) + { + DisposeAbiArrayElements((i, data)); + } + } + } + + internal static void DisposeMarshalerArray(object box) => ((MarshalerArray)box).Dispose(); + + internal static unsafe void DisposeAbiArray(object box) + { + if (box == null) return; + var abi = ((int length, IntPtr data))box; + if (abi.data == IntPtr.Zero) return; + DisposeAbiArrayElements(abi); + Marshal.FreeCoTaskMem(abi.data); + } + + private static unsafe void DisposeAbiArrayElements((int length, IntPtr data) abi) + { + var data = (byte*)abi.data.ToPointer(); + var abi_element_size = sizeof(Type); + for (int i = 0; i < abi.length; i++) + { + var abi_element = Unsafe.ReadUnaligned(data); + DisposeAbi(abi_element); + data += abi_element_size; + } + } + } +}