Background and motivation
In interop scenarios where we have unbounded generic APIs that can take blittable or non-blittable types, we can end up in a scenario where we have a generic type T that has a corresponding type TAbi. However, there is no way to represent TAbi as a generic argument, so we have a mechanism to retrieve it as a System.Type instance. This allows us to still reason about the type.
If we want to do anything with the type though (like copy values around, go from a pointer into a buffer to a managed representation of it), we end up needing to use the APIs on System.Runtime.InteropServices.Marshal that take a System.Type, which are slow and not AOT-compatible.
We hit this scenario in CsWinRT when we are trying to marshal from WinRT a T[] where T is non-blittable. Today CsWinRT uses the APIs on Marshal even though the type will always be blittable as there's no alternative APIs.
API Proposal
namespace System.Runtime.CompilerServices;
public static class RuntimeHelpers
{
+ public object? Box(ref byte target, RuntimeTypeHandle type);
}
API Usage
int length = ...;
IntPtr data = ...;
if (data == IntPtr.Zero)
{
return null;
}
var array = new T[length];
var data = (byte*)data.ToPointer();
var abi_element_size = Marshal.SizeOf(AbiType); // #97344 API would go here to get rid of the remaining usage of Marshal.SizeOf
for (int i = 0; i < abi.length; i++)
{
array[i] = Marshaler<T>.FromAbi(RuntimeHelpers.Box(ref *data, AbiType.TypeHandle));
data += abi_element_size;
}
return array;
Alternative Designs
Mentioned below in the collapsable section.
Risks
Minimal risk.
Previous Proposal
API Proposal
namespace System;
public ref struct TypedReference
{
+ public TypedReference(ref byte target, RuntimeTypeHandle type);
}
API Usage
int length = ...;
IntPtr data = ...;
if (data == IntPtr.Zero)
{
return null;
}
var array = new T[length];
var data = (byte*)data.ToPointer();
var abi_element_size = Marshal.SizeOf(AbiType); // #97344 API would go here to get rid of the remaining usage of Marshal.SizeOf
for (int i = 0; i < abi.length; i++)
{
var abi_element_ref = new TypedReference(ref *data, AbiType.TypeHandle);
array[i] = Marshaler<T>.FromAbi(abi_element_ref.ToObject());
data += abi_element_size;
}
return array;
Alternative Designs
We could provide another API on RuntimeHelpers to read and box an unmanaged value-type from a buffer, or even provide a higher level API over a ReadOnlySpan<byte>, like a MemoryMarshal.Read(ref ReadOnlySpan<byte>, System.Type) method.
We could make this method a static method with an Unsafe suffix to further describe its unsafeness.
Risks
System.TypedReference is a rarely used type. There may be issues in the runtime implementations around supporting it.
Background and motivation
In interop scenarios where we have unbounded generic APIs that can take blittable or non-blittable types, we can end up in a scenario where we have a generic type
Tthat has a corresponding typeTAbi. However, there is no way to representTAbias a generic argument, so we have a mechanism to retrieve it as aSystem.Typeinstance. This allows us to still reason about the type.If we want to do anything with the type though (like copy values around, go from a pointer into a buffer to a managed representation of it), we end up needing to use the APIs on
System.Runtime.InteropServices.Marshalthat take aSystem.Type, which are slow and not AOT-compatible.We hit this scenario in CsWinRT when we are trying to marshal from WinRT a
T[]whereTis non-blittable. Today CsWinRT uses the APIs on Marshal even though the type will always be blittable as there's no alternative APIs.API Proposal
namespace System.Runtime.CompilerServices; public static class RuntimeHelpers { + public object? Box(ref byte target, RuntimeTypeHandle type); }API Usage
Alternative Designs
Mentioned below in the collapsable section.
Risks
Minimal risk.
Previous Proposal
API Proposal
namespace System; public ref struct TypedReference { + public TypedReference(ref byte target, RuntimeTypeHandle type); }API Usage
Alternative Designs
We could provide another API on
RuntimeHelpersto read and box an unmanaged value-type from a buffer, or even provide a higher level API over aReadOnlySpan<byte>, like aMemoryMarshal.Read(ref ReadOnlySpan<byte>, System.Type)method.We could make this method a static method with an
Unsafesuffix to further describe its unsafeness.Risks
System.TypedReferenceis a rarely used type. There may be issues in the runtime implementations around supporting it.