Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make marshalling 'System.Type' AOT-safe #1459

Merged
merged 2 commits into from
Jan 23, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
182 changes: 182 additions & 0 deletions src/WinRT.Runtime/FundamentalMarshalers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
// Licensed under the MIT License.

using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

namespace ABI.System
{
Expand Down Expand Up @@ -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)
{
}
Expand Down Expand Up @@ -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<T>. 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
Sergio0694 marked this conversation as resolved.
Show resolved Hide resolved
{
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<Type>(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<Type>(data);
DisposeAbi(abi_element);
data += abi_element_size;
}
}
}
}

43 changes: 35 additions & 8 deletions src/WinRT.Runtime/Marshalers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -875,6 +875,12 @@ private static Type GetAbiType()
return null;
}

// Exclude Type, as it has dedicated marshalling code available from Marshaler<T>
if (typeof(T) == typeof(Type))
{
throw new NotSupportedException("Using 'System.Type' with MarshalNonBlittable<T> isn't supported, use Marshaler<T> instead.");
}

// Fallback path with the original logic
return typeof(T).GetAbiType();
}
Expand All @@ -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();
Expand Down Expand Up @@ -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)
Expand All @@ -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;
Expand All @@ -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)
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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();
Expand All @@ -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;
Expand Down Expand Up @@ -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<T>.CreateMarshalerArray(array);
GetAbiArray = MarshalNonBlittable<T>.GetAbiArray;
FromAbiArray = MarshalNonBlittable<T>.FromAbiArray;
FromManagedArray = MarshalNonBlittable<T>.FromManagedArray;
CopyManagedArray = MarshalNonBlittable<T>.CopyManagedArray;
DisposeMarshalerArray = MarshalNonBlittable<T>.DisposeMarshalerArray;
DisposeAbiArray = MarshalNonBlittable<T>.DisposeAbiArray;
CreateMarshalerArray = (Func<T[], object>)(object)new Func<Type[], object>(ABI.System.NonBlittableMarshallingStubs.Type_CreateMarshalerArray);
GetAbiArray = new Func<object, (int, IntPtr)>(ABI.System.Type.GetAbiArray);
FromAbiArray = (Func<object, T[]>)(object)new Func<object, Type[]>(ABI.System.Type.FromAbiArray);
FromManagedArray = (Func<T[], (int, IntPtr)>)(object)new Func<Type[], (int, IntPtr)>(ABI.System.Type.FromManagedArray);
CopyManagedArray = (Action<T[], IntPtr>)(object)new Action<Type[], IntPtr>(ABI.System.Type.CopyManagedArray);
DisposeMarshalerArray = new Action<object>(ABI.System.Type.DisposeMarshalerArray);
DisposeAbiArray = new Action<object>(ABI.System.Type.DisposeAbiArray);
}
else if (typeof(T).IsValueType)
{
Expand Down
2 changes: 1 addition & 1 deletion src/WinRT.Runtime/Projections/Type.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ internal enum TypeKind : int
#else
public
#endif
struct Type
partial struct Type
{
private IntPtr Name;
private TypeKind Kind;
Expand Down