未验证 提交 5e94a46c 编写于 作者: J Jeremy Koritzinsky 提交者: GitHub

Add the approved span marshallers and remove the v1 model (#71989)

* Add SpanMarshaller and ReadOnlySpanMarshaller. Move the new attribute types to System.Runtime ref assembly

* Remove pinning of the managed type. The new model isn't supposed to support this and it was breaking the span marshallers.

* Delete the V1 marshalling model

You served us well, but your time has passed.

* Remove obsoleted unit tests. Rename type usages in JS generator

* Fix test build

* PR feedback and add doc comments.

* Fix some extraneous usages of the v1 API in tests.

* Remove test of the managed type's GetPinnableReference method. We don't support this with the v2 system.
上级 12dd537a
......@@ -877,14 +877,12 @@
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\Marshalling\BStrStringMarshaller.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\Marshalling\ContiguousCollectionMarshallerAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\Marshalling\CustomMarshallerAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\Marshalling\CustomTypeMarshallerAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\Marshalling\CustomTypeMarshallerDirection.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\Marshalling\CustomTypeMarshallerFeatures.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\Marshalling\CustomTypeMarshallerKind.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\Marshalling\MarshalMode.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\Marshalling\MarshalUsingAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\Marshalling\NativeMarshallingAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\Marshalling\PointerArrayMarshaller.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\Marshalling\ReadOnlySpanMarshaller.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\Marshalling\SpanMarshaller.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\Marshalling\Utf16StringMarshaller.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\Marshalling\Utf8StringMarshaller.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\MemoryMarshal.cs" />
......
......@@ -4,6 +4,7 @@
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Marshalling;
using System.Runtime.Versioning;
using EditorBrowsableAttribute = System.ComponentModel.EditorBrowsableAttribute;
......@@ -20,6 +21,9 @@ namespace System
[DebuggerTypeProxy(typeof(SpanDebugView<>))]
[DebuggerDisplay("{ToString(),raw}")]
[NonVersionable]
#pragma warning disable SYSLIB1056 // Specified native type is invalid
[NativeMarshalling(typeof(ReadOnlySpanMarshaller<,>))]
#pragma warning restore SYSLIB1056 // Specified native type is invalid
public readonly ref struct ReadOnlySpan<T>
{
/// <summary>A byref or a native ptr.</summary>
......
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
namespace System.Runtime.InteropServices.Marshalling
{
/// <summary>
/// Attribute used to indicate that the type can be used to convert a value of the provided <see cref="ManagedType"/> to a native representation.
/// </summary>
/// <remarks>
/// This attribute is recognized by the runtime-provided source generators for source-generated interop scenarios.
/// It is not used by the interop marshalling system at runtime.
/// </remarks>
/// <seealso cref="LibraryImportAttribute"/>
[AttributeUsage(AttributeTargets.Struct)]
public sealed class CustomTypeMarshallerAttribute : Attribute
{
public CustomTypeMarshallerAttribute(Type managedType, CustomTypeMarshallerKind marshallerKind = CustomTypeMarshallerKind.Value)
{
ManagedType = managedType;
MarshallerKind = marshallerKind;
}
/// <summary>
/// The managed type for which the attributed type is a marshaller
/// </summary>
public Type ManagedType { get; }
/// <summary>
/// The required shape of the attributed type
/// </summary>
public CustomTypeMarshallerKind MarshallerKind { get; }
/// <summary>
/// When the <see cref="CustomTypeMarshallerFeatures.CallerAllocatedBuffer"/> flag is set on <see cref="Features"/> the size of the caller-allocated buffer in number of elements.
/// </summary>
public int BufferSize { get; set; }
/// <summary>
/// The marshalling directions this custom type marshaller supports.
/// </summary>
/// <remarks>Default is <see cref="CustomTypeMarshallerDirection.Ref"/></remarks>
public CustomTypeMarshallerDirection Direction { get; set; } = CustomTypeMarshallerDirection.Ref;
/// <summary>
/// The optional features for the <see cref="MarshallerKind"/> that the marshaller supports.
/// </summary>
public CustomTypeMarshallerFeatures Features { get; set; }
/// <summary>
/// This type is used as a placeholder for the first generic parameter when generic parameters cannot be used
/// to identify the managed type (i.e. when the marshaller type is generic over T and the managed type is T[])
/// </summary>
public struct GenericPlaceholder
{
}
}
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
#if MICROSOFT_INTEROP_SOURCEGENERATION
namespace Microsoft.Interop
#else
namespace System.Runtime.InteropServices.Marshalling
#endif
{
/// <summary>
/// Optional features supported by custom type marshallers.
/// </summary>
[Flags]
public enum CustomTypeMarshallerFeatures
{
/// <summary>
/// No optional features supported
/// </summary>
None = 0,
/// <summary>
/// The marshaller owns unmanaged resources that must be freed
/// </summary>
UnmanagedResources = 0x1,
/// <summary>
/// The marshaller can use a caller-allocated buffer instead of allocating in some scenarios
/// </summary>
CallerAllocatedBuffer = 0x2,
/// <summary>
/// The marshaller uses the two-stage marshalling design for its <see cref="CustomTypeMarshallerKind"/> instead of the one-stage design.
/// </summary>
TwoStageMarshalling = 0x4
}
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
#if MICROSOFT_INTEROP_SOURCEGENERATION
namespace Microsoft.Interop
#else
namespace System.Runtime.InteropServices.Marshalling
#endif
{
/// <summary>
/// The shape of a custom type marshaller for usage in source-generated interop scenarios.
/// </summary>
/// <seealso cref="LibraryImportAttribute"/>
public enum CustomTypeMarshallerKind
{
/// <summary>
/// This custom type marshaller represents a single value.
/// </summary>
Value,
/// <summary>
/// This custom type marshaller represents a container of values that are placed sequentially in memory.
/// </summary>
LinearCollection
}
}
......@@ -10,7 +10,7 @@ namespace System.Runtime.InteropServices.Marshalling
/// This attribute is recognized by the runtime-provided source generators for source-generated interop scenarios.
/// It is not used by the interop marshalling system at runtime.
/// <seealso cref="LibraryImportAttribute"/>
/// <seealso cref="CustomTypeMarshallerAttribute" />
/// <seealso cref="CustomMarshallerAttribute" />
/// </remarks>
[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.ReturnValue, AllowMultiple = true)]
public sealed class MarshalUsingAttribute : Attribute
......@@ -26,7 +26,7 @@ public MarshalUsingAttribute()
/// <summary>
/// Create a <see cref="MarshalUsingAttribute" /> that provides a native marshalling type and optionally size information.
/// </summary>
/// <param name="nativeType">The marshaller type used to convert the attributed type from managed to native code. This type must be attributed with <see cref="CustomTypeMarshallerAttribute" /></param>
/// <param name="nativeType">The marshaller type used to convert the attributed type from managed to native code. This type must be attributed with <see cref="CustomMarshallerAttribute" /></param>
public MarshalUsingAttribute(Type nativeType)
: this()
{
......@@ -34,7 +34,7 @@ public MarshalUsingAttribute(Type nativeType)
}
/// <summary>
/// The marshaller type used to convert the attributed type from managed to native code. This type must be attributed with <see cref="CustomTypeMarshallerAttribute" />
/// The marshaller type used to convert the attributed type from managed to native code. This type must be attributed with <see cref="CustomMarshallerAttribute" />
/// </summary>
public Type? NativeType { get; }
......
......@@ -10,7 +10,7 @@ namespace System.Runtime.InteropServices.Marshalling
/// This attribute is recognized by the runtime-provided source generators for source-generated interop scenarios.
/// It is not used by the interop marshalling system at runtime.
/// <seealso cref="LibraryImportAttribute"/>
/// <seealso cref="CustomTypeMarshallerAttribute" />
/// <seealso cref="CustomMarshallerAttribute" />
/// </remarks>
[AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class | AttributeTargets.Enum | AttributeTargets.Delegate)]
public sealed class NativeMarshallingAttribute : Attribute
......@@ -18,14 +18,14 @@ public sealed class NativeMarshallingAttribute : Attribute
/// <summary>
/// Create a <see cref="NativeMarshallingAttribute" /> that provides a native marshalling type.
/// </summary>
/// <param name="nativeType">The marshaller type used to convert the attributed type from managed to native code. This type must be attributed with <see cref="CustomTypeMarshallerAttribute" /></param>
/// <param name="nativeType">The marshaller type used to convert the attributed type from managed to native code. This type must be attributed with <see cref="CustomMarshallerAttribute" /></param>
public NativeMarshallingAttribute(Type nativeType)
{
NativeType = nativeType;
}
/// <summary>
/// The marshaller type used to convert the attributed type from managed to native code. This type must be attributed with <see cref="CustomTypeMarshallerAttribute" />
/// The marshaller type used to convert the attributed type from managed to native code. This type must be attributed with <see cref="CustomMarshallerAttribute" />
/// </summary>
public Type NativeType { get; }
}
......
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Text;
namespace System.Runtime.InteropServices.Marshalling
{
/// <summary>
/// The <see cref="ReadOnlySpanMarshaller{T, TUnmanagedElement}"/> type supports marshalling a <see cref="ReadOnlySpan{T}"/> from managed value
/// to a contiguous native array of the unmanaged values of the elements.
/// </summary>
/// <typeparam name="T">The managed element type of the span.</typeparam>
/// <typeparam name="TUnmanagedElement">The unmanaged type for the elements of the span.</typeparam>
/// <remarks>
/// A <see cref="ReadOnlySpan{T}"/> marshalled with this marshaller will match the semantics of <see cref="MemoryMarshal.GetReference{T}(ReadOnlySpan{T})"/>.
/// In particular, this marshaller will pass a non-null value for a zero-length span if the span was constructed with a non-null value.
/// </remarks>
[CLSCompliant(false)]
[CustomMarshaller(typeof(ReadOnlySpan<>), MarshalMode.ManagedToUnmanagedIn, typeof(ReadOnlySpanMarshaller<,>.ManagedToUnmanagedIn))]
[CustomMarshaller(typeof(ReadOnlySpan<>), MarshalMode.UnmanagedToManagedOut, typeof(ReadOnlySpanMarshaller<,>.UnmanagedToManagedOut))]
[ContiguousCollectionMarshaller]
public static unsafe class ReadOnlySpanMarshaller<T, TUnmanagedElement>
where TUnmanagedElement : unmanaged
{
/// <summary>
/// The marshaller type that supports marshalling from managed into unmanaged in a call from unmanaged code to managed code.
/// </summary>
public static unsafe class UnmanagedToManagedOut
{
/// <summary>
/// Allocate the space to store the unmanaged elements.
/// </summary>
/// <param name="managed">The managed span.</param>
/// <param name="numElements">The number of elements in the span.</param>
/// <returns>A pointer to the block of memory for the unmanaged elements.</returns>
public static TUnmanagedElement* AllocateContainerForUnmanagedElements(ReadOnlySpan<T> managed, out int numElements)
{
// Emulate the pinning behavior:
// If the span is over a null reference, then pass a null pointer.
if (Unsafe.IsNullRef(ref MemoryMarshal.GetReference(managed)))
{
numElements = 0;
return null;
}
numElements = managed.Length;
// Always allocate at least one byte when the array is zero-length.
int spaceToAllocate = Math.Max(checked(sizeof(TUnmanagedElement) * numElements), 1);
return (TUnmanagedElement*)Marshal.AllocCoTaskMem(spaceToAllocate);
}
/// <summary>
/// Get a span of the managed collection elements.
/// </summary>
/// <param name="managed">The managed collection.</param>
/// <returns>A span of the managed collection elements.</returns>
public static ReadOnlySpan<T> GetManagedValuesSource(ReadOnlySpan<T> managed)
=> managed;
/// <summary>
/// Get a span of the space where the unmanaged collection elements should be stored.
/// </summary>
/// <param name="unmanaged">The pointer to the block of memory for the unmanaged elements.</param>
/// <param name="numElements">The number of elements that will be copied into the memory block.</param>
/// <returns>A span over the unmanaged memory that can contain the specified number of elements.</returns>
public static Span<TUnmanagedElement> GetUnmanagedValuesDestination(TUnmanagedElement* unmanaged, int numElements)
=> new Span<TUnmanagedElement>(unmanaged, numElements);
}
/// <summary>
/// The marshaller type that supports marshalling from managed into unmanaged in a call from managed code to unmanaged code.
/// </summary>
public ref struct ManagedToUnmanagedIn
{
/// <summary>
/// The size of the caller-allocated buffer to allocate.
/// </summary>
// We'll keep the buffer size at a maximum of 200 bytes to avoid overflowing the stack.
public static int BufferSize { get; } = 0x200 / sizeof(TUnmanagedElement);
private ReadOnlySpan<T> _managedArray;
private TUnmanagedElement* _allocatedMemory;
private Span<TUnmanagedElement> _span;
/// <summary>
/// Initializes the <see cref="SpanMarshaller{T, TUnmanagedElement}.ManagedToUnmanagedIn"/> marshaller.
/// </summary>
/// <param name="managed">Span to be marshalled.</param>
/// <param name="buffer">Buffer that may be used for marshalling.</param>
/// <remarks>
/// The <paramref name="buffer"/> must not be movable - that is, it should not be
/// on the managed heap or it should be pinned.
/// </remarks>
public void FromManaged(ReadOnlySpan<T> managed, Span<TUnmanagedElement> buffer)
{
_allocatedMemory = null;
// Emulate the pinning behavior:
// If the span is over a null reference, then pass a null pointer.
if (Unsafe.IsNullRef(ref MemoryMarshal.GetReference(managed)))
{
_managedArray = null;
_span = default;
return;
}
_managedArray = managed;
// Always allocate at least one byte when the span is zero-length.
if (managed.Length <= buffer.Length)
{
_span = buffer[0..managed.Length];
}
else
{
int bufferSize = checked(managed.Length * sizeof(TUnmanagedElement));
_allocatedMemory = (TUnmanagedElement*)NativeMemory.Alloc((nuint)bufferSize);
_span = new Span<TUnmanagedElement>(_allocatedMemory, managed.Length);
}
}
/// <summary>
/// Gets a span that points to the memory where the managed values of the array are stored.
/// </summary>
/// <returns>Span over managed values of the array.</returns>
public ReadOnlySpan<T> GetManagedValuesSource() => _managedArray;
/// <summary>
/// Returns a span that points to the memory where the unmanaged values of the array should be stored.
/// </summary>
/// <returns>Span where unmanaged values of the array should be stored.</returns>
public Span<TUnmanagedElement> GetUnmanagedValuesDestination() => _span;
/// <summary>
/// Returns a reference to the marshalled array.
/// </summary>
public ref TUnmanagedElement GetPinnableReference() => ref MemoryMarshal.GetReference(_span);
/// <summary>
/// Returns the unmanaged value representing the array.
/// </summary>
public TUnmanagedElement* ToUnmanaged() => (TUnmanagedElement*)Unsafe.AsPointer(ref GetPinnableReference());
/// <summary>
/// Frees resources.
/// </summary>
public void Free()
{
NativeMemory.Free(_allocatedMemory);
}
/// <summary>
/// Pin the managed span to a pointer to pass directly to unmanaged code.
/// </summary>
/// <param name="managed">The managed span.</param>
/// <returns>A reference that can be pinned and directly passed to unmanaged code.</returns>
public static ref T GetPinnableReference(ReadOnlySpan<T> managed)
{
return ref MemoryMarshal.GetReference(managed);
}
}
}
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Text;
namespace System.Runtime.InteropServices.Marshalling
{
/// <summary>
/// The <see cref="SpanMarshaller{T, TUnmanagedElement}"/> type supports marshalling a <see cref="Span{T}"/> from managed value
/// to a contiguous native array of the unmanaged values of the elements.
/// </summary>
/// <typeparam name="T">The managed element type of the span.</typeparam>
/// <typeparam name="TUnmanagedElement">The unmanaged type for the elements of the span.</typeparam>
/// <remarks>
/// A <see cref="Span{T}"/> marshalled with this marshaller will match the semantics of <see cref="MemoryMarshal.GetReference{T}(Span{T})"/>.
/// In particular, this marshaller will pass a non-null value for a zero-length span if the span was constructed with a non-null value.
/// </remarks>
[CLSCompliant(false)]
[CustomMarshaller(typeof(Span<>), MarshalMode.Default, typeof(SpanMarshaller<,>))]
[CustomMarshaller(typeof(Span<>), MarshalMode.ManagedToUnmanagedIn, typeof(SpanMarshaller<,>.ManagedToUnmanagedIn))]
[ContiguousCollectionMarshaller]
public static unsafe class SpanMarshaller<T, TUnmanagedElement>
where TUnmanagedElement : unmanaged
{
/// <summary>
/// Allocate the space to store the unmanaged elements.
/// </summary>
/// <param name="managed">The managed span.</param>
/// <param name="numElements">The number of elements in the span.</param>
/// <returns>A pointer to the block of memory for the unmanaged elements.</returns>
public static TUnmanagedElement* AllocateContainerForUnmanagedElements(Span<T> managed, out int numElements)
{
// Emulate the pinning behavior:
// If the span is over a null reference, then pass a null pointer.
if (Unsafe.IsNullRef(ref MemoryMarshal.GetReference(managed)))
{
numElements = 0;
return null;
}
numElements = managed.Length;
// Always allocate at least one byte when the array is zero-length.
int spaceToAllocate = Math.Max(checked(sizeof(TUnmanagedElement) * numElements), 1);
return (TUnmanagedElement*)Marshal.AllocCoTaskMem(spaceToAllocate);
}
/// <summary>
/// Get a span of the managed collection elements.
/// </summary>
/// <param name="managed">The managed collection.</param>
/// <returns>A span of the managed collection elements.</returns>
public static ReadOnlySpan<T> GetManagedValuesSource(Span<T> managed)
=> managed;
/// <summary>
/// Get a span of the space where the unmanaged collection elements should be stored.
/// </summary>
/// <param name="unmanaged">The pointer to the block of memory for the unmanaged elements.</param>
/// <param name="numElements">The number of elements that will be copied into the memory block.</param>
/// <returns>A span over the unmanaged memory that can contain the specified number of elements.</returns>
public static Span<TUnmanagedElement> GetUnmanagedValuesDestination(TUnmanagedElement* unmanaged, int numElements)
=> new Span<TUnmanagedElement>(unmanaged, numElements);
/// <summary>
/// Allocate the space to store the managed elements.
/// </summary>
/// <param name="unmanaged">The unmanaged value.</param>
/// <param name="numElements">The number of elements in the unmanaged collection.</param>
/// <returns>A span over enough memory to contain <paramref name="numElements"/> elements.</returns>
public static Span<T> AllocateContainerForManagedElements(TUnmanagedElement* unmanaged, int numElements)
{
if (unmanaged is null)
return null;
return new T[numElements];
}
/// <summary>
/// Get a span of the space where the managed collection elements should be stored.
/// </summary>
/// <param name="managed">A span over the space to store the managed elements</param>
/// <returns>A span over the managed memory that can contain the specified number of elements.</returns>
public static Span<T> GetManagedValuesDestination(Span<T> managed)
=> managed;
/// <summary>
/// Get a span of the native collection elements.
/// </summary>
/// <param name="unmanaged">The unmanaged value.</param>
/// <param name="numElements">The number of elements in the unmanaged collection.</param>
/// <returns>A span over the native collection elements.</returns>
public static ReadOnlySpan<TUnmanagedElement> GetUnmanagedValuesSource(TUnmanagedElement* unmanaged, int numElements)
=> new ReadOnlySpan<TUnmanagedElement>(unmanaged, numElements);
/// <summary>
/// Free the allocated unmanaged memory.
/// </summary>
/// <param name="unmanaged">Allocated unmanaged memory</param>
public static void Free(TUnmanagedElement* unmanaged)
=> Marshal.FreeCoTaskMem((IntPtr)unmanaged);
/// <summary>
/// The marshaller type that supports marshalling from managed into unmanaged in a call from managed code to unmanaged code.
/// </summary>
public ref struct ManagedToUnmanagedIn
{
// We'll keep the buffer size at a maximum of 200 bytes to avoid overflowing the stack.
public static int BufferSize { get; } = 0x200 / sizeof(TUnmanagedElement);
private Span<T> _managedArray;
private TUnmanagedElement* _allocatedMemory;
private Span<TUnmanagedElement> _span;
/// <summary>
/// Initializes the <see cref="SpanMarshaller{T, TUnmanagedElement}.ManagedToUnmanagedIn"/> marshaller.
/// </summary>
/// <param name="managed">Span to be marshalled.</param>
/// <param name="buffer">Buffer that may be used for marshalling.</param>
/// <remarks>
/// The <paramref name="buffer"/> must not be movable - that is, it should not be
/// on the managed heap or it should be pinned.
/// </remarks>
public void FromManaged(Span<T> managed, Span<TUnmanagedElement> buffer)
{
_allocatedMemory = null;
// Emulate the pinning behavior:
// If the span is over a null reference, then pass a null pointer.
if (Unsafe.IsNullRef(ref MemoryMarshal.GetReference(managed)))
{
_managedArray = null;
_span = default;
return;
}
_managedArray = managed;
if (managed.Length <= buffer.Length)
{
_span = buffer[0..managed.Length];
}
else
{
int bufferSize = checked(managed.Length * sizeof(TUnmanagedElement));
_allocatedMemory = (TUnmanagedElement*)NativeMemory.Alloc((nuint)bufferSize);
_span = new Span<TUnmanagedElement>(_allocatedMemory, managed.Length);
}
}
/// <summary>
/// Gets a span that points to the memory where the managed values of the array are stored.
/// </summary>
/// <returns>Span over managed values of the array.</returns>
public ReadOnlySpan<T> GetManagedValuesSource() => _managedArray;
/// <summary>
/// Returns a span that points to the memory where the unmanaged values of the array should be stored.
/// </summary>
/// <returns>Span where unmanaged values of the array should be stored.</returns>
public Span<TUnmanagedElement> GetUnmanagedValuesDestination() => _span;
/// <summary>
/// Returns a reference to the marshalled array.
/// </summary>
public ref TUnmanagedElement GetPinnableReference() => ref MemoryMarshal.GetReference(_span);
/// <summary>
/// Returns the unmanaged value representing the array.
/// </summary>
public TUnmanagedElement* ToUnmanaged() => (TUnmanagedElement*)Unsafe.AsPointer(ref GetPinnableReference());
/// <summary>
/// Frees resources.
/// </summary>
public void Free()
{
NativeMemory.Free(_allocatedMemory);
}
public static ref T GetPinnableReference(Span<T> managed)
{
return ref MemoryMarshal.GetReference(managed);
}
}
}
}
......@@ -4,6 +4,7 @@
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Marshalling;
using System.Runtime.Versioning;
using EditorBrowsableAttribute = System.ComponentModel.EditorBrowsableAttribute;
using EditorBrowsableState = System.ComponentModel.EditorBrowsableState;
......@@ -19,6 +20,9 @@ namespace System
[DebuggerTypeProxy(typeof(SpanDebugView<>))]
[DebuggerDisplay("{ToString(),raw}")]
[NonVersionable]
#pragma warning disable SYSLIB1056 // Specified native type is invalid
[NativeMarshalling(typeof(SpanMarshaller<,>))]
#pragma warning restore SYSLIB1056 // Specified native type is invalid
public readonly ref struct Span<T>
{
/// <summary>A byref or a native ptr.</summary>
......
......@@ -25,7 +25,7 @@ public override (string managed, string native) GetIdentifiers(TypePositionInfo
public JSImportCodeContext(JSImportData attributeData, StubCodeContext inner)
{
_inner = inner;
Direction = CustomTypeMarshallerDirection.In;
Direction = CustomTypeMarshallingDirection.In;
AttributeData = attributeData;
}
......@@ -37,7 +37,7 @@ public JSImportCodeContext(JSImportData attributeData, StubCodeContext inner)
public JSExportCodeContext(JSExportData attributeData, StubCodeContext inner)
{
_inner = inner;
Direction = CustomTypeMarshallerDirection.Out;
Direction = CustomTypeMarshallingDirection.Out;
AttributeData = attributeData;
}
......
......@@ -53,12 +53,12 @@ public override IEnumerable<StatementSyntax> Generate(TypePositionInfo info, Stu
.Select(a => ParseTypeName(a.FullTypeName))
.ToArray();
if (context.CurrentStage == StubCodeContext.Stage.Unmarshal && context.Direction == CustomTypeMarshallerDirection.In && info.IsManagedReturnPosition)
if (context.CurrentStage == StubCodeContext.Stage.Unmarshal && context.Direction == CustomTypeMarshallingDirection.In && info.IsManagedReturnPosition)
{
yield return ToManagedMethod(target, source, jsty);
}
if (context.CurrentStage == StubCodeContext.Stage.Marshal && context.Direction == CustomTypeMarshallerDirection.Out && info.IsManagedReturnPosition)
if (context.CurrentStage == StubCodeContext.Stage.Marshal && context.Direction == CustomTypeMarshallingDirection.Out && info.IsManagedReturnPosition)
{
yield return ToJSMethod(target, source, jsty);
}
......@@ -68,12 +68,12 @@ public override IEnumerable<StatementSyntax> Generate(TypePositionInfo info, Stu
yield return x;
}
if (context.CurrentStage == StubCodeContext.Stage.Invoke && context.Direction == CustomTypeMarshallerDirection.In && !info.IsManagedReturnPosition)
if (context.CurrentStage == StubCodeContext.Stage.Invoke && context.Direction == CustomTypeMarshallingDirection.In && !info.IsManagedReturnPosition)
{
yield return ToJSMethod(target, source, jsty);
}
if (context.CurrentStage == StubCodeContext.Stage.Unmarshal && context.Direction == CustomTypeMarshallerDirection.Out && !info.IsManagedReturnPosition)
if (context.CurrentStage == StubCodeContext.Stage.Unmarshal && context.Direction == CustomTypeMarshallingDirection.Out && !info.IsManagedReturnPosition)
{
yield return ToManagedMethod(target, source, jsty);
}
......
......@@ -32,12 +32,12 @@ public override IEnumerable<StatementSyntax> Generate(TypePositionInfo info, Stu
? Argument(IdentifierName(context.GetIdentifiers(info).native))
: _inner.AsArgument(info, context);
if (context.CurrentStage == StubCodeContext.Stage.Unmarshal && context.Direction == CustomTypeMarshallerDirection.In && info.IsManagedReturnPosition)
if (context.CurrentStage == StubCodeContext.Stage.Unmarshal && context.Direction == CustomTypeMarshallingDirection.In && info.IsManagedReturnPosition)
{
yield return ToManagedMethod(target, source);
}
if (context.CurrentStage == StubCodeContext.Stage.Marshal && context.Direction == CustomTypeMarshallerDirection.Out && info.IsManagedReturnPosition)
if (context.CurrentStage == StubCodeContext.Stage.Marshal && context.Direction == CustomTypeMarshallingDirection.Out && info.IsManagedReturnPosition)
{
yield return ToJSMethod(target, source);
}
......@@ -47,12 +47,12 @@ public override IEnumerable<StatementSyntax> Generate(TypePositionInfo info, Stu
yield return x;
}
if (context.CurrentStage == StubCodeContext.Stage.Invoke && context.Direction == CustomTypeMarshallerDirection.In && !info.IsManagedReturnPosition)
if (context.CurrentStage == StubCodeContext.Stage.Invoke && context.Direction == CustomTypeMarshallingDirection.In && !info.IsManagedReturnPosition)
{
yield return ToJSMethod(target, source);
}
if (context.CurrentStage == StubCodeContext.Stage.Unmarshal && context.Direction == CustomTypeMarshallerDirection.Out && !info.IsManagedReturnPosition)
if (context.CurrentStage == StubCodeContext.Stage.Unmarshal && context.Direction == CustomTypeMarshallingDirection.Out && !info.IsManagedReturnPosition)
{
yield return ToManagedMethod(target, source);
}
......
......@@ -45,14 +45,14 @@ public override IEnumerable<StatementSyntax> Generate(TypePositionInfo info, Stu
? Argument(IdentifierName(context.GetIdentifiers(info).native))
: _inner.AsArgument(info, context);
if (context.CurrentStage == StubCodeContext.Stage.Unmarshal && context.Direction == CustomTypeMarshallerDirection.In && info.IsManagedReturnPosition)
if (context.CurrentStage == StubCodeContext.Stage.Unmarshal && context.Direction == CustomTypeMarshallingDirection.In && info.IsManagedReturnPosition)
{
yield return jsty.ResultTypeInfo.FullTypeName == "void"
? ToManagedMethodVoid(target, source)
: ToManagedMethod(target, source, jsty.ResultTypeInfo.Syntax);
}
if (context.CurrentStage == StubCodeContext.Stage.Marshal && context.Direction == CustomTypeMarshallerDirection.Out && info.IsManagedReturnPosition)
if (context.CurrentStage == StubCodeContext.Stage.Marshal && context.Direction == CustomTypeMarshallingDirection.Out && info.IsManagedReturnPosition)
{
yield return jsty.ResultTypeInfo.FullTypeName == "void"
? ToJSMethodVoid(target, source)
......@@ -64,14 +64,14 @@ public override IEnumerable<StatementSyntax> Generate(TypePositionInfo info, Stu
yield return x;
}
if (context.CurrentStage == StubCodeContext.Stage.Invoke && context.Direction == CustomTypeMarshallerDirection.In && !info.IsManagedReturnPosition)
if (context.CurrentStage == StubCodeContext.Stage.Invoke && context.Direction == CustomTypeMarshallingDirection.In && !info.IsManagedReturnPosition)
{
yield return jsty.ResultTypeInfo.FullTypeName == "void"
? ToJSMethodVoid(target, source)
: ToJSMethod(target, source, jsty.ResultTypeInfo.Syntax);
}
if (context.CurrentStage == StubCodeContext.Stage.Unmarshal && context.Direction == CustomTypeMarshallerDirection.Out && !info.IsManagedReturnPosition)
if (context.CurrentStage == StubCodeContext.Stage.Unmarshal && context.Direction == CustomTypeMarshallingDirection.Out && !info.IsManagedReturnPosition)
{
yield return jsty.ResultTypeInfo.FullTypeName == "void"
? ToManagedMethodVoid(target, source)
......
......@@ -22,7 +22,6 @@ namespace Microsoft.Interop.Analyzers
public class CustomTypeMarshallerFixer : CodeFixProvider
{
private const string AddMissingCustomTypeMarshallerMembersKey = nameof(AddMissingCustomTypeMarshallerMembersKey);
private const string AddMissingCustomTypeMarshallerFeaturesKey = nameof(AddMissingCustomTypeMarshallerFeaturesKey);
private sealed class CustomFixAllProvider : DocumentBasedFixAllProvider
{
......@@ -41,15 +40,7 @@ protected override async Task<Document> FixAllAsync(FixAllContext fixAllContext,
{
SyntaxNode node = root.FindNode(diagnosticsBySpan.Key);
AddMissingMembers(fixAllContext, editor, diagnosticsBySpan, node);
}
break;
case AddMissingCustomTypeMarshallerFeaturesKey:
foreach (IGrouping<TextSpan, Diagnostic> diagnosticsBySpan in diagnostics.GroupBy(d => d.Location.SourceSpan))
{
SyntaxNode node = root.FindNode(diagnosticsBySpan.Key);
await AddMissingFeatures(fixAllContext, editor, diagnosticsBySpan, node).ConfigureAwait(false);
AddMissingMembers(editor, diagnosticsBySpan, node);
}
break;
default:
......@@ -59,7 +50,7 @@ protected override async Task<Document> FixAllAsync(FixAllContext fixAllContext,
return editor.GetChangedDocument();
}
private static void AddMissingMembers(FixAllContext fixAllContext, DocumentEditor editor, IEnumerable<Diagnostic> diagnostics, SyntaxNode node)
private static void AddMissingMembers(DocumentEditor editor, IEnumerable<Diagnostic> diagnostics, SyntaxNode node)
{
var (missingMemberNames, _) = GetRequiredShapeMissingMemberNames(diagnostics);
ITypeSymbol marshallerType = (ITypeSymbol)editor.SemanticModel.GetDeclaredSymbol(node);
......@@ -73,24 +64,6 @@ private static void AddMissingMembers(FixAllContext fixAllContext, DocumentEdito
editor.SemanticModel.Compilation,
gen));
}
private static async Task AddMissingFeatures(FixAllContext fixAllContext, DocumentEditor editor, IEnumerable<Diagnostic> diagnostics, SyntaxNode node)
{
var (featuresToAdd, _) = GetFeaturesToAdd(diagnostics);
ITypeSymbol marshallerType = (ITypeSymbol)editor.SemanticModel.GetDeclaredSymbol(node);
AttributeData customTypeMarshallerAttribute = marshallerType.GetAttributes().FirstOrDefault(attr => attr.AttributeClass.ToDisplayString() == TypeNames.CustomTypeMarshallerAttribute);
SyntaxNode attributeSyntax = await customTypeMarshallerAttribute.ApplicationSyntaxReference!.GetSyntaxAsync(fixAllContext.CancellationToken).ConfigureAwait(false);
editor.ReplaceNode(
attributeSyntax,
(node, gen) =>
CustomTypeMarshallerFixer.AddMissingFeatures(
gen.GetName(node),
customTypeMarshallerAttribute,
featuresToAdd,
gen));
}
}
public override FixAllProvider? GetFixAllProvider() => new CustomFixAllProvider();
......@@ -120,18 +93,6 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context)
AddMissingCustomTypeMarshallerMembersKey),
missingMembersDiagnostics);
}
var (featuresToAdd, featuresToAddDiagnostics) = GetFeaturesToAdd(context.Diagnostics);
if (featuresToAddDiagnostics.Count > 0 && featuresToAdd != CustomTypeMarshallerFeatures.None)
{
context.RegisterCodeFix(
CodeAction.Create(
SR.AddMissingFeaturesToCustomTypeMarshaller,
ct => AddMissingFeatures(doc, node, featuresToAdd, ct),
AddMissingCustomTypeMarshallerFeaturesKey),
featuresToAddDiagnostics);
}
}
private static (List<string> missingMembers, List<Diagnostic> fixedDiagnostics) GetRequiredShapeMissingMemberNames(IEnumerable<Diagnostic> diagnostics)
......@@ -152,78 +113,6 @@ private static (List<string> missingMembers, List<Diagnostic> fixedDiagnostics)
return (missingMemberNames, requiredShapeDiagnostics);
}
private static (CustomTypeMarshallerFeatures featuresToAdd, List<Diagnostic> fixedDiagnostics) GetFeaturesToAdd(IEnumerable<Diagnostic> diagnostics)
{
List<Diagnostic> featuresToAddDiagnostics = new();
CustomTypeMarshallerFeatures featuresToAdd = CustomTypeMarshallerFeatures.None;
foreach (var diagnostic in diagnostics)
{
if (diagnostic.Id == AnalyzerDiagnostics.Ids.ProvidedMethodsNotSpecifiedInFeatures)
{
featuresToAddDiagnostics.Add(diagnostic);
if (diagnostic.Properties.TryGetValue(CustomTypeMarshallerAnalyzer.MissingFeaturesKey, out string missingFeatures)
&& Enum.TryParse(missingFeatures, out CustomTypeMarshallerFeatures featuresValue))
{
featuresToAdd |= featuresValue;
}
}
}
return (featuresToAdd, featuresToAddDiagnostics);
}
private static async Task<Document> AddMissingFeatures(Document doc, SyntaxNode node, CustomTypeMarshallerFeatures featuresToAdd, CancellationToken ct)
{
var editor = await DocumentEditor.CreateAsync(doc, ct).ConfigureAwait(false);
var gen = editor.Generator;
ISymbol marshallerType = editor.SemanticModel.GetDeclaredSymbol(node, ct);
AttributeData customTypeMarshallerAttribute = marshallerType.GetAttributes().FirstOrDefault(attr => attr.AttributeClass.ToDisplayString() == TypeNames.CustomTypeMarshallerAttribute);
SyntaxNode attributeSyntax = await customTypeMarshallerAttribute.ApplicationSyntaxReference!.GetSyntaxAsync(ct).ConfigureAwait(false);
SyntaxNode updatedDeclaration = AddMissingFeatures(gen.GetName(attributeSyntax), customTypeMarshallerAttribute, featuresToAdd, gen);
editor.ReplaceNode(attributeSyntax, updatedDeclaration);
return editor.GetChangedDocument();
}
private static SyntaxNode AddMissingFeatures(string attributeName, AttributeData? customTypeMarshallerAttribute, CustomTypeMarshallerFeatures featuresToAdd, SyntaxGenerator gen)
{
SyntaxNode newAttributeSyntax = gen.Attribute(attributeName);
newAttributeSyntax = gen.AddAttributeArguments(newAttributeSyntax, customTypeMarshallerAttribute.ConstructorArguments.Select(a => gen.AttributeArgument(gen.TypedConstantExpression(a))));
CustomTypeMarshallerFeatures newFeaturesValue = featuresToAdd;
int? featuresArgLocation = null;
newAttributeSyntax = gen.AddAttributeArguments(newAttributeSyntax, customTypeMarshallerAttribute.NamedArguments
.Where((a, i) =>
{
if (a.Key == "Features")
{
// Capture the original location of the 'Features' named argument so we can update it "in place".
featuresArgLocation = customTypeMarshallerAttribute.ConstructorArguments.Length + i;
newFeaturesValue |= (CustomTypeMarshallerFeatures)a.Value.Value;
return false;
}
return true;
})
.Select(a => gen.AttributeArgument(a.Key, gen.TypedConstantExpression(a.Value))));
SyntaxNode featureAttributeArgument = gen.AttributeArgument("Features",
gen.GetEnumValueAsFlagsExpression(
customTypeMarshallerAttribute.AttributeClass.GetMembers(ManualTypeMarshallingHelper_V1.CustomMarshallerAttributeFields.Features).OfType<IPropertySymbol>().First().Type,
(int)newFeaturesValue,
includeZeroValueFlags: false));
newAttributeSyntax = featuresArgLocation is null
? gen.AddAttributeArguments(newAttributeSyntax, new[] { featureAttributeArgument })
: gen.InsertAttributeArguments(newAttributeSyntax, featuresArgLocation.Value, new[] { featureAttributeArgument });
return newAttributeSyntax;
}
private static async Task<Document> AddMissingMembers(Document doc, SyntaxNode node, List<string> missingMemberNames, CancellationToken ct)
{
......@@ -240,165 +129,8 @@ private static async Task<Document> AddMissingMembers(Document doc, SyntaxNode n
private static SyntaxNode AddMissingMembers(SyntaxNode node, ITypeSymbol
marshallerType, List<string> missingMemberNames, Compilation compilation, SyntaxGenerator gen)
{
INamedTypeSymbol @byte = compilation.GetSpecialType(SpecialType.System_Byte);
INamedTypeSymbol @object = compilation.GetSpecialType(SpecialType.System_Object);
INamedTypeSymbol spanOfT = compilation.GetTypeByMetadataName(TypeNames.System_Span_Metadata)!;
INamedTypeSymbol spanOfByte = spanOfT.Construct(@byte)!;
INamedTypeSymbol readOnlySpanOfT = compilation.GetTypeByMetadataName(TypeNames.System_ReadOnlySpan_Metadata)!;
INamedTypeSymbol readOnlySpanOfByte = readOnlySpanOfT.Construct(@byte)!;
INamedTypeSymbol int32 = compilation.GetSpecialType(SpecialType.System_Int32);
SyntaxNode updatedDeclaration = node;
(_, ITypeSymbol managedType, _) = ManualTypeMarshallingHelper_V1.GetMarshallerShapeInfo(marshallerType);
IMethodSymbol? fromNativeValueMethod = ManualTypeMarshallingHelper_V1.FindFromNativeValueMethod(marshallerType);
IMethodSymbol? toNativeValueMethod = ManualTypeMarshallingHelper_V1.FindToNativeValueMethod(marshallerType);
IMethodSymbol? getManagedValuesSourceMethod = ManualTypeMarshallingHelper_V1.FindGetManagedValuesSourceMethod(marshallerType, readOnlySpanOfT);
IMethodSymbol? getManagedValuesDestinationMethod = ManualTypeMarshallingHelper_V1.FindGetManagedValuesDestinationMethod(marshallerType, spanOfT);
SyntaxNode[] throwNotImplementedStatements = new[]
{
gen.ThrowStatement(gen.ObjectCreationExpression(gen.DottedName("System.NotImplementedException")))
};
foreach (string missingMemberName in missingMemberNames)
{
switch (missingMemberName)
{
case CustomTypeMarshallerAnalyzer.MissingMemberNames.ValueManagedToNativeConstructor:
updatedDeclaration = gen.AddMembers(updatedDeclaration, gen.ConstructorDeclaration(
gen.GetName(node),
new[]
{
gen.ParameterDeclaration("managed", type: gen.TypeExpression(managedType))
},
accessibility: Accessibility.Public,
statements: throwNotImplementedStatements));
break;
case CustomTypeMarshallerAnalyzer.MissingMemberNames.ValueCallerAllocatedBufferConstructor:
updatedDeclaration = gen.AddMembers(updatedDeclaration, gen.ConstructorDeclaration(
gen.GetName(node),
new[]
{
gen.ParameterDeclaration("managed", type: gen.TypeExpression(managedType)),
gen.ParameterDeclaration("buffer", type: gen.TypeExpression(spanOfByte))
},
accessibility: Accessibility.Public,
statements: throwNotImplementedStatements));
break;
case CustomTypeMarshallerAnalyzer.MissingMemberNames.CollectionManagedToNativeConstructor:
updatedDeclaration = gen.AddMembers(updatedDeclaration, gen.ConstructorDeclaration(
gen.GetName(node),
new[]
{
gen.ParameterDeclaration("managed", type: gen.TypeExpression(managedType)),
gen.ParameterDeclaration("nativeElementSize", type: gen.TypeExpression(int32))
},
accessibility: Accessibility.Public,
statements: throwNotImplementedStatements));
break;
case CustomTypeMarshallerAnalyzer.MissingMemberNames.CollectionCallerAllocatedBufferConstructor:
updatedDeclaration = gen.AddMembers(updatedDeclaration, gen.ConstructorDeclaration(
gen.GetName(node),
new[]
{
gen.ParameterDeclaration("managed", type: gen.TypeExpression(managedType)),
gen.ParameterDeclaration("buffer", type: gen.TypeExpression(spanOfByte)),
gen.ParameterDeclaration("nativeElementSize", type: gen.TypeExpression(int32))
},
accessibility: Accessibility.Public,
statements: throwNotImplementedStatements));
break;
case CustomTypeMarshallerAnalyzer.MissingMemberNames.CollectionNativeElementSizeConstructor:
updatedDeclaration = gen.AddMembers(updatedDeclaration, gen.ConstructorDeclaration(
gen.GetName(node),
new[]
{
gen.ParameterDeclaration("nativeElementSize", type: gen.TypeExpression(int32))
},
accessibility: Accessibility.Public,
statements: throwNotImplementedStatements));
break;
case ShapeMemberNames_V1.Value.ToManaged:
updatedDeclaration = gen.AddMembers(updatedDeclaration, gen.MethodDeclaration(
ShapeMemberNames_V1.Value.ToManaged,
returnType: gen.TypeExpression(managedType),
accessibility: Accessibility.Public,
statements: throwNotImplementedStatements));
break;
case ShapeMemberNames_V1.Value.FreeNative:
updatedDeclaration = gen.AddMembers(updatedDeclaration, gen.MethodDeclaration(ShapeMemberNames_V1.Value.FreeNative,
accessibility: Accessibility.Public,
statements: throwNotImplementedStatements));
break;
case ShapeMemberNames_V1.Value.FromNativeValue:
updatedDeclaration = gen.AddMembers(updatedDeclaration, gen.MethodDeclaration(
ShapeMemberNames_V1.Value.FromNativeValue,
parameters: new[]
{
gen.ParameterDeclaration("value",
type: gen.TypeExpression(toNativeValueMethod?.ReturnType ?? @byte))
},
accessibility: Accessibility.Public,
statements: throwNotImplementedStatements));
break;
case ShapeMemberNames_V1.Value.ToNativeValue:
updatedDeclaration = gen.AddMembers(updatedDeclaration, gen.MethodDeclaration(
ShapeMemberNames_V1.Value.ToNativeValue,
returnType: gen.TypeExpression(fromNativeValueMethod?.Parameters[0].Type ?? @byte),
accessibility: Accessibility.Public,
statements: throwNotImplementedStatements));
break;
case ShapeMemberNames_V1.LinearCollection.GetManagedValuesSource:
INamedTypeSymbol? getManagedValuesDestinationReturnType = (INamedTypeSymbol?)getManagedValuesDestinationMethod?.ReturnType;
updatedDeclaration = gen.AddMembers(updatedDeclaration, gen.MethodDeclaration(
ShapeMemberNames_V1.LinearCollection.GetManagedValuesSource,
returnType: gen.TypeExpression(
readOnlySpanOfT.Construct(
getManagedValuesDestinationReturnType?.TypeArguments[0] ?? @object)),
accessibility: Accessibility.Public,
statements: throwNotImplementedStatements));
break;
case ShapeMemberNames_V1.LinearCollection.GetNativeValuesDestination:
updatedDeclaration = gen.AddMembers(updatedDeclaration, gen.MethodDeclaration(
ShapeMemberNames_V1.LinearCollection.GetNativeValuesDestination,
returnType: gen.TypeExpression(spanOfByte),
accessibility: Accessibility.Public,
statements: throwNotImplementedStatements));
break;
case ShapeMemberNames_V1.LinearCollection.GetNativeValuesSource:
updatedDeclaration = gen.AddMembers(updatedDeclaration, gen.MethodDeclaration(
ShapeMemberNames_V1.LinearCollection.GetNativeValuesSource,
parameters: new[]
{
gen.ParameterDeclaration("numElements", type: gen.TypeExpression(int32))
},
returnType: gen.TypeExpression(readOnlySpanOfByte),
accessibility: Accessibility.Public,
statements: throwNotImplementedStatements));
break;
case ShapeMemberNames_V1.LinearCollection.GetManagedValuesDestination:
INamedTypeSymbol? getManagedValuesSourceReturnType = (INamedTypeSymbol?)getManagedValuesSourceMethod?.ReturnType;
updatedDeclaration = gen.AddMembers(updatedDeclaration, gen.MethodDeclaration(
ShapeMemberNames_V1.LinearCollection.GetNativeValuesDestination,
parameters: new[]
{
gen.ParameterDeclaration("numElements", type: gen.TypeExpression(int32))
},
returnType: gen.TypeExpression(
spanOfT.Construct(
getManagedValuesSourceReturnType?.TypeArguments[0] ?? @object)),
accessibility: Accessibility.Public,
statements: throwNotImplementedStatements));
break;
default:
break;
}
}
return updatedDeclaration;
// TODO: Implement adding the missing members for the V2 shapes
return node;
}
}
}
// Licensed to the .NET Foundation under one or more agreements.
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.ComponentModel;
#if MICROSOFT_INTEROP_SOURCEGENERATION
namespace Microsoft.Interop
#else
namespace System.Runtime.InteropServices.Marshalling
#endif
{
/// <summary>
/// A direction of marshalling data into or out of the managed environment
/// </summary>
[Flags]
public enum CustomTypeMarshallerDirection
public enum CustomTypeMarshallingDirection
{
/// <summary>
/// No marshalling direction
......
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Runtime.InteropServices;
using Microsoft.CodeAnalysis;
namespace Microsoft.Interop
{
public readonly record struct CustomTypeMarshallerData_V1(CustomTypeMarshallerKind Kind, CustomTypeMarshallerDirection Direction, CustomTypeMarshallerFeatures Features, int? BufferSize);
public static class ShapeMemberNames_V1
{
public abstract class Value
{
public const string ToNativeValue = nameof(ToNativeValue);
public const string FromNativeValue = nameof(FromNativeValue);
public const string GetPinnableReference = nameof(GetPinnableReference);
public const string FreeNative = nameof(FreeNative);
public const string ToManaged = nameof(ToManaged);
}
public abstract class LinearCollection : Value
{
public const string GetManagedValuesDestination = nameof(GetManagedValuesDestination);
public const string GetManagedValuesSource = nameof(GetManagedValuesSource);
public const string GetNativeValuesDestination = nameof(GetNativeValuesDestination);
public const string GetNativeValuesSource = nameof(GetNativeValuesSource);
}
}
public static class ManualTypeMarshallingHelper_V1
{
public static class CustomMarshallerAttributeFields
{
public const string BufferSize = nameof(BufferSize);
public const string Direction = nameof(Direction);
public const string Features = nameof(Features);
}
public static (bool hasAttribute, ITypeSymbol? managedType, CustomTypeMarshallerData_V1? kind) GetMarshallerShapeInfo(ITypeSymbol marshallerType)
{
var attr = marshallerType.GetAttributes().FirstOrDefault(attr => attr.AttributeClass.ToDisplayString() == TypeNames.CustomTypeMarshallerAttribute);
if (attr is null)
{
return (false, null, null);
}
if (attr.ConstructorArguments.Length == 0)
{
return (true, null, null);
}
CustomTypeMarshallerKind kind = CustomTypeMarshallerKind.Value;
ITypeSymbol? managedType = attr.ConstructorArguments[0].Value as ITypeSymbol;
if (attr.ConstructorArguments.Length > 1)
{
if (attr.ConstructorArguments[1].Value is not int i)
{
return (true, managedType, null);
}
kind = (CustomTypeMarshallerKind)i;
}
var namedArguments = attr.NamedArguments.ToImmutableDictionary();
int? bufferSize = namedArguments.TryGetValue(CustomMarshallerAttributeFields.BufferSize, out TypedConstant bufferSizeConstant) ? bufferSizeConstant.Value as int? : null;
CustomTypeMarshallerDirection direction = namedArguments.TryGetValue(CustomMarshallerAttributeFields.Direction, out TypedConstant directionConstant) ? (CustomTypeMarshallerDirection)directionConstant.Value : CustomTypeMarshallerDirection.Ref;
CustomTypeMarshallerFeatures features = namedArguments.TryGetValue(CustomMarshallerAttributeFields.Features, out TypedConstant featuresConstant) ? (CustomTypeMarshallerFeatures)featuresConstant.Value : CustomTypeMarshallerFeatures.None;
return (true, managedType, new CustomTypeMarshallerData_V1(kind, direction, features, bufferSize));
}
/// <summary>
/// Get the supported <see cref="CustomTypeMarshallerPinning"/> for a marshaller type
/// </summary>
/// <param name="marshallerType">The marshaller type.</param>
/// <param name="managedType">The mananged type that would be marshalled.</param>
/// <returns>Supported <see cref="CustomTypeMarshallerPinning"/></returns>
public static CustomTypeMarshallerPinning GetMarshallerPinningFeatures(ITypeSymbol marshallerType, ITypeSymbol? managedType)
{
CustomTypeMarshallerPinning pinning = CustomTypeMarshallerPinning.None;
if (ManualTypeMarshallingHelper.FindGetPinnableReference(marshallerType) is not null)
{
pinning |= CustomTypeMarshallerPinning.NativeType;
}
if (managedType is not null && ManualTypeMarshallingHelper.FindGetPinnableReference(managedType) is not null)
{
pinning |= CustomTypeMarshallerPinning.ManagedType;
}
return pinning;
}
public static bool HasToManagedMethod(ITypeSymbol nativeType, ITypeSymbol managedType)
{
return nativeType.GetMembers(ShapeMemberNames_V1.Value.ToManaged)
.OfType<IMethodSymbol>()
.Any(m => m.Parameters.IsEmpty
&& !m.ReturnsByRef
&& !m.ReturnsByRefReadonly
&& SymbolEqualityComparer.Default.Equals(m.ReturnType, managedType)
&& !m.IsStatic);
}
public static bool IsManagedToNativeConstructor(
IMethodSymbol ctor,
ITypeSymbol managedType,
CustomTypeMarshallerKind variant)
{
if (variant == CustomTypeMarshallerKind.LinearCollection)
{
return ctor.Parameters.Length == 2
&& SymbolEqualityComparer.Default.Equals(managedType, ctor.Parameters[0].Type)
&& ctor.Parameters[1].Type.SpecialType == SpecialType.System_Int32;
}
return ctor.Parameters.Length == 1
&& SymbolEqualityComparer.Default.Equals(managedType, ctor.Parameters[0].Type);
}
public static bool IsCallerAllocatedSpanConstructor(
IMethodSymbol ctor,
ITypeSymbol managedType,
ITypeSymbol spanOfT,
CustomTypeMarshallerKind variant,
out ITypeSymbol? spanElementType)
{
spanElementType = null;
if (variant == CustomTypeMarshallerKind.LinearCollection)
{
return ctor.Parameters.Length == 3
&& SymbolEqualityComparer.Default.Equals(managedType, ctor.Parameters[0].Type)
&& IsSpanOfUnmanagedType(ctor.Parameters[1].Type, spanOfT, out spanElementType)
&& spanElementType.SpecialType == SpecialType.System_Byte
&& ctor.Parameters[2].Type.SpecialType == SpecialType.System_Int32;
}
return ctor.Parameters.Length == 2
&& SymbolEqualityComparer.Default.Equals(managedType, ctor.Parameters[0].Type)
&& IsSpanOfUnmanagedType(ctor.Parameters[1].Type, spanOfT, out spanElementType);
static bool IsSpanOfUnmanagedType(ITypeSymbol typeToCheck, ITypeSymbol spanOfT, out ITypeSymbol? typeArgument)
{
typeArgument = null;
if (typeToCheck is INamedTypeSymbol namedType
&& SymbolEqualityComparer.Default.Equals(spanOfT, namedType.ConstructedFrom)
&& namedType.TypeArguments.Length == 1
&& namedType.TypeArguments[0].IsUnmanagedType)
{
typeArgument = namedType.TypeArguments[0];
return true;
}
return false;
}
}
public static bool HasFreeNativeMethod(ITypeSymbol type)
{
return type.GetMembers(ShapeMemberNames_V1.Value.FreeNative)
.OfType<IMethodSymbol>()
.Any(m => m is { IsStatic: false, Parameters.Length: 0, ReturnType.SpecialType: SpecialType.System_Void });
}
public static IMethodSymbol? FindToNativeValueMethod(ITypeSymbol type)
{
return type.GetMembers(ShapeMemberNames_V1.Value.ToNativeValue)
.OfType<IMethodSymbol>()
.FirstOrDefault(m => m is { IsStatic: false, Parameters.Length: 0 });
}
public static IMethodSymbol? FindFromNativeValueMethod(ITypeSymbol type)
{
return type.GetMembers(ShapeMemberNames_V1.Value.FromNativeValue)
.OfType<IMethodSymbol>()
.FirstOrDefault(m => m is { IsStatic: false, Parameters.Length: 1, ReturnType.SpecialType: SpecialType.System_Void });
}
public static bool TryGetElementTypeFromLinearCollectionMarshaller(ITypeSymbol type, ITypeSymbol readOnlySpanOfT, out ITypeSymbol elementType)
{
if (FindGetManagedValuesSourceMethod(type, readOnlySpanOfT) is not IMethodSymbol managedValuesSourceMethod)
{
elementType = null!;
return false;
}
elementType = ((INamedTypeSymbol)managedValuesSourceMethod.ReturnType).TypeArguments[0];
return true;
}
public static IMethodSymbol? FindGetManagedValuesSourceMethod(ITypeSymbol type, ITypeSymbol readOnlySpanOfT)
{
return type
.GetMembers(ShapeMemberNames_V1.LinearCollection.GetManagedValuesSource)
.OfType<IMethodSymbol>()
.FirstOrDefault(m => m is { IsStatic: false, ReturnsByRef: false, ReturnsByRefReadonly: false, Parameters.Length: 0, ReturnType: INamedTypeSymbol { ConstructedFrom: INamedTypeSymbol returnType } }
&& SymbolEqualityComparer.Default.Equals(returnType, readOnlySpanOfT));
}
public static IMethodSymbol? FindGetManagedValuesDestinationMethod(ITypeSymbol type, ITypeSymbol spanOfT)
{
return type
.GetMembers(ShapeMemberNames_V1.LinearCollection.GetManagedValuesDestination)
.OfType<IMethodSymbol>()
.FirstOrDefault(m => m is { IsStatic: false, ReturnsByRef: false, ReturnsByRefReadonly: false, Parameters.Length: 1, ReturnType: INamedTypeSymbol { ConstructedFrom: INamedTypeSymbol returnType } }
&& m.Parameters[0].Type.SpecialType == SpecialType.System_Int32
&& SymbolEqualityComparer.Default.Equals(returnType, spanOfT));
}
public static IMethodSymbol? FindGetNativeValuesDestinationMethod(ITypeSymbol type, ITypeSymbol spanOfByte)
{
return type
.GetMembers(ShapeMemberNames_V1.LinearCollection.GetNativeValuesDestination)
.OfType<IMethodSymbol>()
.FirstOrDefault(m => m is { IsStatic: false, ReturnsByRef: false, ReturnsByRefReadonly: false, Parameters.Length: 0, ReturnType: INamedTypeSymbol returnType }
&& SymbolEqualityComparer.Default.Equals(returnType, spanOfByte));
}
public static IMethodSymbol? FindGetNativeValuesSourceMethod(ITypeSymbol type, ITypeSymbol readOnlySpanOfByte)
{
return type
.GetMembers(ShapeMemberNames_V1.LinearCollection.GetNativeValuesSource)
.OfType<IMethodSymbol>()
.FirstOrDefault(m => m is { IsStatic: false, ReturnsByRef: false, ReturnsByRefReadonly: false, Parameters.Length: 1, ReturnType: INamedTypeSymbol returnType }
&& m.Parameters[0].Type.SpecialType == SpecialType.System_Int32
&& SymbolEqualityComparer.Default.Equals(returnType, readOnlySpanOfByte));
}
}
}
......@@ -229,8 +229,7 @@ public static bool HasEntryPointMarshallerAttribute(ITypeSymbol entryPointType)
}
}
if (innerType.ToDisplayString() != TypeNames.CustomTypeMarshallerAttributeGenericPlaceholder
&& innerType.ToDisplayString() != TypeNames.CustomMarshallerAttributeGenericPlaceholder)
if (innerType.ToDisplayString() != TypeNames.CustomMarshallerAttributeGenericPlaceholder)
{
return managedType;
}
......
......@@ -56,31 +56,6 @@ public IMarshallingGenerator Create(TypePositionInfo info, StubCodeContext conte
return CreateCustomNativeTypeMarshaller(info, context, marshalInfo);
}
if (info.MarshallingAttributeInfo is NativeMarshallingAttributeInfo_V1 marshalInfoV1)
{
if (Options.RuntimeMarshallingDisabled || marshalInfoV1.IsStrictlyBlittable)
{
return CreateCustomNativeTypeMarshaller_V1(info, context, marshalInfoV1);
}
if (marshalInfoV1.NativeValueType is SpecialTypeInfo specialType
&& specialType.SpecialType.IsAlwaysBlittable())
{
return CreateCustomNativeTypeMarshaller_V1(info, context, marshalInfoV1);
}
if (marshalInfoV1.NativeValueType is PointerTypeInfo)
{
return CreateCustomNativeTypeMarshaller_V1(info, context, marshalInfoV1);
}
throw new MarshallingNotSupportedException(info, context)
{
NotSupportedDetails = SR.RuntimeMarshallingMustBeDisabled,
DiagnosticProperties = AddDisableRuntimeMarshallingAttributeProperties
};
}
if (info.MarshallingAttributeInfo is UnmanagedBlittableMarshallingInfo blittableInfo)
{
if (Options.RuntimeMarshallingDisabled || blittableInfo.IsStrictlyBlittable)
......@@ -141,11 +116,6 @@ ExpressionSyntax GetExpressionForParam(TypePositionInfo paramInfo)
type = marshallerData.CollectionElementType;
marshallingInfo = marshallerData.CollectionElementMarshallingInfo;
}
else if (marshallingInfo is NativeLinearCollectionMarshallingInfo_V1 collectionInfoV1)
{
type = collectionInfoV1.ElementType;
marshallingInfo = collectionInfoV1.ElementMarshallingInfo;
}
else
{
throw new MarshallingNotSupportedException(info, context)
......@@ -269,9 +239,7 @@ private IMarshallingGenerator CreateCustomNativeTypeMarshaller(TypePositionInfo
marshallingGenerator = new StaticPinnableManagedValueMarshaller(marshallingGenerator, marshallerData.MarshallerType.Syntax);
}
return marshalInfo.IsPinnableManagedType
? new PinnableManagedValueMarshaller(marshallingGenerator)
: marshallingGenerator;
return marshallingGenerator;
}
private IMarshallingGenerator CreateNativeCollectionMarshaller(
......@@ -351,16 +319,13 @@ private IMarshallingGenerator CreateCustomNativeTypeMarshaller(TypePositionInfo
marshallingGenerator = new StaticPinnableManagedValueMarshaller(marshallingGenerator, marshallerTypeSyntax);
}
return marshalInfo.IsPinnableManagedType && elementIsBlittable
? new PinnableManagedValueMarshaller(marshallingGenerator)
: marshallingGenerator;
return marshallingGenerator;
}
private static bool ElementTypeIsSometimesNonBlittable(TypePositionInfo elementInfo)
{
if (elementInfo.MarshallingAttributeInfo is NoMarshallingInfo
|| elementInfo.MarshallingAttributeInfo is UnmanagedBlittableMarshallingInfo { IsStrictlyBlittable: true }
|| elementInfo.MarshallingAttributeInfo is NativeMarshallingAttributeInfo_V1 { IsStrictlyBlittable: true })
|| elementInfo.MarshallingAttributeInfo is UnmanagedBlittableMarshallingInfo { IsStrictlyBlittable: true })
{
return false;
}
......@@ -419,159 +384,6 @@ private void ValidateCustomNativeTypeMarshallingSupported(TypePositionInfo info,
}
}
private IMarshallingGenerator CreateCustomNativeTypeMarshaller_V1(TypePositionInfo info, StubCodeContext context, NativeMarshallingAttributeInfo_V1 marshalInfo)
{
ValidateCustomNativeTypeMarshallingSupported_V1(info, context, marshalInfo);
ICustomNativeTypeMarshallingStrategy marshallingStrategy = new SimpleCustomNativeTypeMarshalling(marshalInfo.NativeMarshallingType.Syntax);
if ((marshalInfo.MarshallingFeatures & CustomTypeMarshallerFeatures.CallerAllocatedBuffer) != 0)
{
if (marshalInfo.BufferSize is null)
{
throw new MarshallingNotSupportedException(info, context);
}
marshallingStrategy = new StackallocOptimizationMarshalling(marshallingStrategy, marshalInfo.BufferElementType.Syntax, marshalInfo.BufferSize.Value);
}
if ((marshalInfo.MarshallingFeatures & CustomTypeMarshallerFeatures.UnmanagedResources) != 0)
{
marshallingStrategy = new FreeNativeCleanupStrategy(marshallingStrategy);
}
// Collections have extra configuration, so handle them here.
if (marshalInfo is NativeLinearCollectionMarshallingInfo_V1 collectionMarshallingInfo)
{
return CreateNativeCollectionMarshaller_V1(info, context, collectionMarshallingInfo, marshallingStrategy);
}
else if (marshalInfo.NativeValueType is not null)
{
marshallingStrategy = new CustomNativeTypeWithToFromNativeValueMarshalling(marshallingStrategy, marshalInfo.NativeValueType.Syntax);
if (marshalInfo.PinningFeatures.HasFlag(CustomTypeMarshallerPinning.NativeType) && marshalInfo.MarshallingFeatures.HasFlag(CustomTypeMarshallerFeatures.TwoStageMarshalling))
{
marshallingStrategy = new PinnableMarshallerTypeMarshalling(marshallingStrategy);
}
}
IMarshallingGenerator marshallingGenerator = new CustomNativeTypeMarshallingGenerator(marshallingStrategy, enableByValueContentsMarshalling: false);
if (marshalInfo.PinningFeatures.HasFlag(CustomTypeMarshallerPinning.ManagedType))
{
return new PinnableManagedValueMarshaller(marshallingGenerator);
}
return marshallingGenerator;
}
private static void ValidateCustomNativeTypeMarshallingSupported_V1(TypePositionInfo info, StubCodeContext context, NativeMarshallingAttributeInfo_V1 marshalInfo)
{
// The marshalling method for this type doesn't support marshalling from native to managed,
// but our scenario requires marshalling from native to managed.
if ((info.RefKind == RefKind.Ref || info.RefKind == RefKind.Out || info.IsManagedReturnPosition)
&& !marshalInfo.Direction.HasFlag(CustomTypeMarshallerDirection.Out))
{
throw new MarshallingNotSupportedException(info, context)
{
NotSupportedDetails = string.Format(SR.CustomTypeMarshallingNativeToManagedUnsupported, marshalInfo.NativeMarshallingType.FullTypeName)
};
}
// The marshalling method for this type doesn't support marshalling from managed to native by value,
// but our scenario requires marshalling from managed to native by value.
if (!info.IsByRef
&& !info.IsManagedReturnPosition
&& context.SingleFrameSpansNativeContext
&& !(marshalInfo.PinningFeatures.HasFlag(CustomTypeMarshallerPinning.ManagedType)
|| marshalInfo.MarshallingFeatures.HasFlag(CustomTypeMarshallerFeatures.CallerAllocatedBuffer)
|| marshalInfo.Direction.HasFlag(CustomTypeMarshallerDirection.In)))
{
throw new MarshallingNotSupportedException(info, context)
{
NotSupportedDetails = string.Format(SR.CustomTypeMarshallingManagedToNativeUnsupported, marshalInfo.NativeMarshallingType.FullTypeName)
};
}
// The marshalling method for this type doesn't support marshalling from managed to native by reference,
// but our scenario requires marshalling from managed to native by reference.
// "in" byref supports stack marshalling.
if (info.RefKind == RefKind.In
&& !(context.SingleFrameSpansNativeContext && marshalInfo.MarshallingFeatures.HasFlag(CustomTypeMarshallerFeatures.CallerAllocatedBuffer))
&& !marshalInfo.Direction.HasFlag(CustomTypeMarshallerDirection.In))
{
throw new MarshallingNotSupportedException(info, context)
{
NotSupportedDetails = string.Format(SR.CustomTypeMarshallingManagedToNativeUnsupported, marshalInfo.NativeMarshallingType.FullTypeName)
};
}
// The marshalling method for this type doesn't support marshalling from managed to native by reference,
// but our scenario requires marshalling from managed to native by reference.
// "ref" byref marshalling doesn't support stack marshalling
// The "Out" direction for "ref" was checked above
if (info.RefKind == RefKind.Ref
&& !marshalInfo.Direction.HasFlag(CustomTypeMarshallerDirection.In))
{
throw new MarshallingNotSupportedException(info, context)
{
NotSupportedDetails = string.Format(SR.CustomTypeMarshallingManagedToNativeUnsupported, marshalInfo.NativeMarshallingType.FullTypeName)
};
}
}
private IMarshallingGenerator CreateNativeCollectionMarshaller_V1(
TypePositionInfo info,
StubCodeContext context,
NativeLinearCollectionMarshallingInfo_V1 collectionInfo,
ICustomNativeTypeMarshallingStrategy marshallingStrategy)
{
var elementInfo = new TypePositionInfo(collectionInfo.ElementType, collectionInfo.ElementMarshallingInfo)
{
ManagedIndex = info.ManagedIndex,
RefKind = CreateElementRefKind(info.RefKind, info.ByValueContentsMarshalKind)
};
IMarshallingGenerator elementMarshaller = _elementMarshallingGenerator.Create(
elementInfo,
new LinearCollectionElementMarshallingCodeContext(StubCodeContext.Stage.Setup, string.Empty, string.Empty, context));
ExpressionSyntax numElementsExpression = LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(0));
if (info.IsManagedReturnPosition || (info.IsByRef && info.RefKind != RefKind.In))
{
// In this case, we need a numElementsExpression supplied from metadata, so we'll calculate it here.
numElementsExpression = GetNumElementsExpressionFromMarshallingInfo(info, collectionInfo.ElementCountInfo, context);
}
bool elementIsBlittable = elementMarshaller is BlittableMarshaller;
if (elementIsBlittable)
{
marshallingStrategy = new LinearCollectionWithBlittableElementsMarshalling(marshallingStrategy, collectionInfo.ElementType.Syntax, numElementsExpression);
}
else
{
marshallingStrategy = new LinearCollectionWithNonBlittableElementsMarshalling(marshallingStrategy, elementMarshaller, elementInfo, numElementsExpression);
}
marshallingStrategy = new CustomNativeTypeWithToFromNativeValueMarshalling(marshallingStrategy, collectionInfo.NativeValueType.Syntax);
if (collectionInfo.PinningFeatures.HasFlag(CustomTypeMarshallerPinning.NativeType) && collectionInfo.MarshallingFeatures.HasFlag(CustomTypeMarshallerFeatures.TwoStageMarshalling))
{
marshallingStrategy = new PinnableMarshallerTypeMarshalling(marshallingStrategy);
}
TypeSyntax nativeElementType = elementMarshaller.AsNativeType(elementInfo);
marshallingStrategy = new SizeOfElementMarshalling(
marshallingStrategy,
SizeOfExpression(nativeElementType));
IMarshallingGenerator marshallingGenerator = new CustomNativeTypeMarshallingGenerator(marshallingStrategy, enableByValueContentsMarshalling: false);
// Elements in the collection must be blittable to use the pinnable marshaller.
if (collectionInfo.PinningFeatures.HasFlag(CustomTypeMarshallerPinning.ManagedType) && elementIsBlittable)
{
return new PinnableManagedValueMarshaller(marshallingGenerator);
}
return marshallingGenerator;
}
private static RefKind CreateElementRefKind(RefKind refKind, ByValueContentsMarshalKind byValueContentsMarshalKind)
{
if (refKind == RefKind.None)
......
......@@ -14,10 +14,10 @@ namespace Microsoft.Interop
/// </summary>
internal sealed class CustomNativeTypeMarshallingGenerator : IMarshallingGenerator
{
private readonly ICustomTypeMarshallingStrategyBase _nativeTypeMarshaller;
private readonly ICustomTypeMarshallingStrategy _nativeTypeMarshaller;
private readonly bool _enableByValueContentsMarshalling;
public CustomNativeTypeMarshallingGenerator(ICustomTypeMarshallingStrategyBase nativeTypeMarshaller, bool enableByValueContentsMarshalling)
public CustomNativeTypeMarshallingGenerator(ICustomTypeMarshallingStrategy nativeTypeMarshaller, bool enableByValueContentsMarshalling)
{
_nativeTypeMarshaller = nativeTypeMarshaller;
_enableByValueContentsMarshalling = enableByValueContentsMarshalling;
......@@ -54,14 +54,7 @@ public IEnumerable<StatementSyntax> Generate(TypePositionInfo info, StubCodeCont
case StubCodeContext.Stage.Marshal:
if (!info.IsManagedReturnPosition && info.RefKind != RefKind.Out)
{
if (_nativeTypeMarshaller is ICustomNativeTypeMarshallingStrategy strategyWithConstructorArgs)
{
return strategyWithConstructorArgs.GenerateMarshalStatements(info, context, strategyWithConstructorArgs.GetNativeTypeConstructorArguments(info, context));
}
else if (_nativeTypeMarshaller is ICustomTypeMarshallingStrategy strategyWithNoConstructorArgs)
{
return strategyWithNoConstructorArgs.GenerateMarshalStatements(info, context);
}
return _nativeTypeMarshaller.GenerateMarshalStatements(info, context);
}
break;
case StubCodeContext.Stage.Pin:
......
......@@ -13,7 +13,7 @@ namespace Microsoft.Interop
/// <summary>
/// The base interface for implementing various aspects of the custom native type and collection marshalling specs.
/// </summary>
internal interface ICustomTypeMarshallingStrategyBase
internal interface ICustomTypeMarshallingStrategy
{
TypeSyntax AsNativeType(TypePositionInfo info);
......@@ -30,10 +30,7 @@ internal interface ICustomTypeMarshallingStrategyBase
IEnumerable<StatementSyntax> GenerateUnmarshalStatements(TypePositionInfo info, StubCodeContext context);
bool UsesNativeIdentifier(TypePositionInfo info, StubCodeContext context);
}
internal interface ICustomTypeMarshallingStrategy : ICustomTypeMarshallingStrategyBase
{
IEnumerable<StatementSyntax> GenerateMarshalStatements(TypePositionInfo info, StubCodeContext context);
IEnumerable<StatementSyntax> GenerateGuaranteedUnmarshalStatements(TypePositionInfo info, StubCodeContext context);
IEnumerable<StatementSyntax> GenerateNotifyForSuccessfulInvokeStatements(TypePositionInfo info, StubCodeContext context);
......
......@@ -272,15 +272,18 @@ public bool AnyIncomingEdge(int to)
public static IEnumerable<TypePositionInfo> GetDependentElementsOfMarshallingInfo(
MarshallingInfo elementMarshallingInfo)
{
if (elementMarshallingInfo is NativeLinearCollectionMarshallingInfo_V1 nestedCollection)
if (elementMarshallingInfo is NativeLinearCollectionMarshallingInfo nestedCollection)
{
if (nestedCollection.ElementCountInfo is CountElementCountInfo { ElementInfo: TypePositionInfo nestedCountElement })
{
yield return nestedCountElement;
}
foreach (TypePositionInfo nestedElements in GetDependentElementsOfMarshallingInfo(nestedCollection.ElementMarshallingInfo))
foreach (KeyValuePair<MarshalMode, CustomTypeMarshallerData> mode in nestedCollection.Marshallers.Modes)
{
yield return nestedElements;
foreach (TypePositionInfo nestedElements in GetDependentElementsOfMarshallingInfo(mode.Value.CollectionElementMarshallingInfo))
{
yield return nestedElements;
}
}
}
}
......
......@@ -9,16 +9,8 @@
<!-- Code included from System.Runtime.InteropServices -->
<ItemGroup>
<Compile Include="$(CoreLibSharedDir)\System\Runtime\InteropServices\Marshalling\CustomTypeMarshallerKind.cs"
Link="Production\CustomTypeMarshallerKind.cs" />
<Compile Include="$(CoreLibSharedDir)\System\Runtime\InteropServices\Marshalling\CustomTypeMarshallerDirection.cs"
Link="Production\CustomTypeMarshallerDirection.cs" />
<Compile Include="$(CoreLibSharedDir)\System\Runtime\InteropServices\Marshalling\CustomTypeMarshallerFeatures.cs"
Link="Production\CustomTypeMarshallerFeatures.cs" />
<Compile Include="$(CoreLibSharedDir)System\Runtime\InteropServices\StringMarshalling.cs"
Link="Production\StringMarshalling.cs" />
<Compile Include="$(CoreLibSharedDir)\System\Runtime\InteropServices\Marshalling\MarshalMode.cs"
Link="Production\MarshalMode.cs" />
<Compile Include="$(CoreLibSharedDir)System\Runtime\InteropServices\StringMarshalling.cs" Link="Production\StringMarshalling.cs" />
<Compile Include="$(CoreLibSharedDir)\System\Runtime\InteropServices\Marshalling\MarshalMode.cs" Link="Production\MarshalMode.cs" />
</ItemGroup>
<ItemGroup>
......
......@@ -81,10 +81,10 @@ public enum Stage
public Stage CurrentStage { get; init; } = Stage.Invalid;
/// <summary>
/// <c>CustomTypeMarshallerDirection.In</c> means method import like <c>[LibraryImport]</c>.
/// <c>CustomTypeMarshallerDirection.Out</c> means method export like in <c>[UnmanagedCallersOnly]</c> or in <c>[JSExport]</c>
/// <c>CustomTypeMarshallingDirection.In</c> means method import like <c>[LibraryImport]</c>.
/// <c>CustomTypeMarshallingDirection.Out</c> means method export like in <c>[UnmanagedCallersOnly]</c> or in <c>[JSExport]</c>
/// </summary>
public CustomTypeMarshallerDirection Direction { get; init; } = CustomTypeMarshallerDirection.In;
public CustomTypeMarshallingDirection Direction { get; init; } = CustomTypeMarshallingDirection.In;
/// <summary>
/// Gets the currently targeted framework and version for stub code generation.
......
......@@ -13,9 +13,6 @@ public static class TypeNames
public const string MarshalUsingAttribute = "System.Runtime.InteropServices.Marshalling.MarshalUsingAttribute";
public const string CustomTypeMarshallerAttribute = "System.Runtime.InteropServices.Marshalling.CustomTypeMarshallerAttribute";
public const string CustomTypeMarshallerAttributeGenericPlaceholder = "System.Runtime.InteropServices.Marshalling.CustomTypeMarshallerAttribute.GenericPlaceholder";
public const string CustomMarshallerAttribute = "System.Runtime.InteropServices.Marshalling.CustomMarshallerAttribute";
public const string CustomMarshallerAttributeGenericPlaceholder = CustomMarshallerAttribute + ".GenericPlaceholder";
public const string ContiguousCollectionMarshallerAttribute = "System.Runtime.InteropServices.Marshalling.ContiguousCollectionMarshallerAttribute";
......
......@@ -2200,72 +2200,6 @@ public static unsafe class BStrStringMarshaller
public void Free() { throw null; }
}
}
[System.AttributeUsageAttribute(System.AttributeTargets.Struct | System.AttributeTargets.Class)]
public sealed partial class ContiguousCollectionMarshallerAttribute : System.Attribute
{
}
[System.AttributeUsageAttribute(System.AttributeTargets.Struct | System.AttributeTargets.Class, AllowMultiple = true)]
public sealed partial class CustomMarshallerAttribute : System.Attribute
{
public CustomMarshallerAttribute(System.Type managedType, System.Runtime.InteropServices.Marshalling.MarshalMode marshalMode, System.Type marshallerType) { }
public System.Type ManagedType { get { throw null; } }
public System.Runtime.InteropServices.Marshalling.MarshalMode MarshalMode { get { throw null; } }
public System.Type MarshallerType { get { throw null; } }
public struct GenericPlaceholder
{
}
}
[System.AttributeUsageAttribute(System.AttributeTargets.Struct)]
public sealed partial class CustomTypeMarshallerAttribute : System.Attribute
{
public CustomTypeMarshallerAttribute(System.Type managedType, System.Runtime.InteropServices.Marshalling.CustomTypeMarshallerKind marshallerKind = System.Runtime.InteropServices.Marshalling.CustomTypeMarshallerKind.Value) { }
public System.Type ManagedType { get { throw null; } }
public System.Runtime.InteropServices.Marshalling.CustomTypeMarshallerKind MarshallerKind { get { throw null; } }
public int BufferSize { get { throw null; } set { } }
public System.Runtime.InteropServices.Marshalling.CustomTypeMarshallerDirection Direction { get { throw null; } set { } }
public System.Runtime.InteropServices.Marshalling.CustomTypeMarshallerFeatures Features { get { throw null; } set { } }
public struct GenericPlaceholder
{
}
}
[System.FlagsAttribute]
public enum CustomTypeMarshallerDirection
{
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
None = 0,
In = 0x1,
Out = 0x2,
Ref = In | Out,
}
[System.FlagsAttribute]
public enum CustomTypeMarshallerFeatures
{
None = 0,
UnmanagedResources = 0x1,
CallerAllocatedBuffer = 0x2,
TwoStageMarshalling = 0x4
}
public enum CustomTypeMarshallerKind
{
Value,
LinearCollection
}
public enum MarshalMode
{
Default = 0,
ManagedToUnmanagedIn = 1,
ManagedToUnmanagedRef = 2,
ManagedToUnmanagedOut = 3,
UnmanagedToManagedIn = 4,
UnmanagedToManagedRef = 5,
UnmanagedToManagedOut = 6,
ElementIn = 7,
ElementRef = 8,
ElementOut = 9
}
[System.AttributeUsageAttribute(System.AttributeTargets.Parameter | System.AttributeTargets.ReturnValue, AllowMultiple = true)]
public sealed partial class MarshalUsingAttribute : System.Attribute
{
......@@ -2277,12 +2211,6 @@ public sealed partial class MarshalUsingAttribute : System.Attribute
public int ElementIndirectionDepth { get { throw null; } set { } }
public const string ReturnsCountValue = "return-value";
}
[System.AttributeUsageAttribute(System.AttributeTargets.Struct | System.AttributeTargets.Class | System.AttributeTargets.Enum | System.AttributeTargets.Delegate)]
public sealed partial class NativeMarshallingAttribute : System.Attribute
{
public NativeMarshallingAttribute(System.Type nativeType) { }
public System.Type NativeType { get { throw null; } }
}
[System.CLSCompliant(false)]
[System.Runtime.InteropServices.Marshalling.CustomMarshallerAttribute(typeof(CustomMarshallerAttribute.GenericPlaceholder*[]),
System.Runtime.InteropServices.Marshalling.MarshalMode.Default,
......
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Diagnostics;
using System.Runtime.CompilerServices;
namespace System.Runtime.InteropServices.Marshalling
{
// Stack-alloc threshold set to 256 bytes to enable small arrays to be passed on the stack.
// Number kept small to ensure that P/Invokes with a lot of array parameters doesn't
// blow the stack since this is a new optimization in the code-generated interop.
[CustomTypeMarshaller(typeof(ReadOnlySpan<>), CustomTypeMarshallerKind.LinearCollection, Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.UnmanagedResources | CustomTypeMarshallerFeatures.CallerAllocatedBuffer | CustomTypeMarshallerFeatures.TwoStageMarshalling, BufferSize = 0x200)]
public unsafe ref struct ReadOnlySpanMarshaller<T>
{
private ReadOnlySpan<T> _managedSpan;
private readonly int _sizeOfNativeElement;
private IntPtr _allocatedMemory;
public ReadOnlySpanMarshaller(int sizeOfNativeElement)
: this()
{
_sizeOfNativeElement = sizeOfNativeElement;
}
public ReadOnlySpanMarshaller(ReadOnlySpan<T> managed, int sizeOfNativeElement)
:this(managed, Span<byte>.Empty, sizeOfNativeElement)
{
}
public ReadOnlySpanMarshaller(ReadOnlySpan<T> managed, Span<byte> stackSpace, int sizeOfNativeElement)
{
_allocatedMemory = default;
_sizeOfNativeElement = sizeOfNativeElement;
if (managed.Length == 0)
{
_managedSpan = default;
NativeValueStorage = default;
return;
}
_managedSpan = managed;
int spaceToAllocate = managed.Length * sizeOfNativeElement;
if (spaceToAllocate <= stackSpace.Length)
{
NativeValueStorage = stackSpace[0..spaceToAllocate];
}
else
{
_allocatedMemory = Marshal.AllocCoTaskMem(spaceToAllocate);
NativeValueStorage = new Span<byte>((void*)_allocatedMemory, spaceToAllocate);
}
}
public ReadOnlySpan<T> GetManagedValuesSource() => _managedSpan;
public Span<byte> GetNativeValuesDestination() => NativeValueStorage;
private Span<byte> NativeValueStorage { get; }
public ref byte GetPinnableReference() => ref MemoryMarshal.GetReference(NativeValueStorage);
public byte* ToNativeValue() => (byte*)Unsafe.AsPointer(ref GetPinnableReference());
public void FreeNative()
{
Marshal.FreeCoTaskMem(_allocatedMemory);
}
}
[CustomTypeMarshaller(typeof(Span<>), CustomTypeMarshallerKind.LinearCollection, Features = CustomTypeMarshallerFeatures.UnmanagedResources | CustomTypeMarshallerFeatures.CallerAllocatedBuffer | CustomTypeMarshallerFeatures.TwoStageMarshalling, BufferSize = 0x200)]
public unsafe ref struct SpanMarshaller<T>
{
private Span<T> _managedSpan;
private readonly int _sizeOfNativeElement;
private IntPtr _allocatedMemory;
public SpanMarshaller(int sizeOfNativeElement)
: this()
{
_sizeOfNativeElement = sizeOfNativeElement;
}
public SpanMarshaller(Span<T> managed, int sizeOfNativeElement)
:this(managed, Span<byte>.Empty, sizeOfNativeElement)
{
}
public SpanMarshaller(Span<T> managed, Span<byte> stackSpace, int sizeOfNativeElement)
{
_allocatedMemory = default;
_sizeOfNativeElement = sizeOfNativeElement;
if (managed.Length == 0)
{
_managedSpan = default;
NativeValueStorage = default;
return;
}
_managedSpan = managed;
int spaceToAllocate = managed.Length * sizeOfNativeElement;
if (spaceToAllocate <= stackSpace.Length)
{
NativeValueStorage = stackSpace[0..spaceToAllocate];
}
else
{
_allocatedMemory = Marshal.AllocCoTaskMem(spaceToAllocate);
NativeValueStorage = new Span<byte>((void*)_allocatedMemory, spaceToAllocate);
}
}
private Span<byte> NativeValueStorage { get; set; }
public ReadOnlySpan<T> GetManagedValuesSource() => _managedSpan;
public Span<T> GetManagedValuesDestination(int length) => _managedSpan = new T[length];
public Span<byte> GetNativeValuesDestination() => NativeValueStorage;
public ReadOnlySpan<byte> GetNativeValuesSource(int length) => NativeValueStorage = new Span<byte>((void*)_allocatedMemory, length * _sizeOfNativeElement);
public ref byte GetPinnableReference() => ref NativeValueStorage.GetPinnableReference();
public byte* ToNativeValue() => (byte*)Unsafe.AsPointer(ref GetPinnableReference());
public void FromNativeValue(byte* value) => _allocatedMemory = (IntPtr)value;
public Span<T> ToManaged()
{
return _managedSpan;
}
public void FreeNative()
{
Marshal.FreeCoTaskMem(_allocatedMemory);
}
}
[CustomTypeMarshaller(typeof(Span<>), CustomTypeMarshallerKind.LinearCollection, Features = CustomTypeMarshallerFeatures.UnmanagedResources | CustomTypeMarshallerFeatures.CallerAllocatedBuffer | CustomTypeMarshallerFeatures.TwoStageMarshalling, BufferSize = 0x200)]
public unsafe ref struct NeverNullSpanMarshaller<T>
{
private SpanMarshaller<T> _inner;
public NeverNullSpanMarshaller(int sizeOfNativeElement)
: this()
{
_inner = new SpanMarshaller<T>(sizeOfNativeElement);
}
public NeverNullSpanMarshaller(Span<T> managed, int sizeOfNativeElement)
{
_inner = new SpanMarshaller<T>(managed, sizeOfNativeElement);
}
public NeverNullSpanMarshaller(Span<T> managed, Span<byte> stackSpace, int sizeOfNativeElement)
{
_inner = new SpanMarshaller<T>(managed, stackSpace, sizeOfNativeElement);
}
public ReadOnlySpan<T> GetManagedValuesSource() => _inner.GetManagedValuesSource();
public Span<T> GetManagedValuesDestination(int length) => _inner.GetManagedValuesDestination(length);
public Span<byte> GetNativeValuesDestination() => _inner.GetNativeValuesDestination();
public ReadOnlySpan<byte> GetNativeValuesSource(int length) => _inner.GetNativeValuesSource(length);
public ref byte GetPinnableReference() => ref _inner.GetPinnableReference();
public byte* ToNativeValue() => _inner.GetManagedValuesSource().Length == 0 ? (byte*)0xa5a5a5a5 : (byte*)Unsafe.AsPointer(ref GetPinnableReference());
public void FromNativeValue(byte* value) => _inner.FromNativeValue(value);
public Span<T> ToManaged() => _inner.ToManaged();
public void FreeNative()
{
_inner.FreeNative();
}
}
[CustomTypeMarshaller(typeof(ReadOnlySpan<>), CustomTypeMarshallerKind.LinearCollection, Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.UnmanagedResources | CustomTypeMarshallerFeatures.CallerAllocatedBuffer | CustomTypeMarshallerFeatures.TwoStageMarshalling, BufferSize = 0x200)]
public unsafe ref struct NeverNullReadOnlySpanMarshaller<T>
{
private ReadOnlySpanMarshaller<T> _inner;
public NeverNullReadOnlySpanMarshaller(int sizeOfNativeElement)
: this()
{
_inner = new ReadOnlySpanMarshaller<T>(sizeOfNativeElement);
}
public NeverNullReadOnlySpanMarshaller(ReadOnlySpan<T> managed, int sizeOfNativeElement)
{
_inner = new ReadOnlySpanMarshaller<T>(managed, sizeOfNativeElement);
}
public NeverNullReadOnlySpanMarshaller(ReadOnlySpan<T> managed, Span<byte> stackSpace, int sizeOfNativeElement)
{
_inner = new ReadOnlySpanMarshaller<T>(managed, stackSpace, sizeOfNativeElement);
}
public ReadOnlySpan<T> GetManagedValuesSource() => _inner.GetManagedValuesSource();
public Span<byte> GetNativeValuesDestination() => _inner.GetNativeValuesDestination();
public ref byte GetPinnableReference() => ref _inner.GetPinnableReference();
public byte* ToNativeValue() => _inner.GetManagedValuesSource().Length == 0 ? (byte*)0xa5a5a5a5 : (byte*)Unsafe.AsPointer(ref GetPinnableReference());
public void FreeNative()
{
_inner.FreeNative();
}
}
// Stack-alloc threshold set to 0 so that the generator can use the constructor that takes a stackSpace to let the marshaller know that the original data span can be used and safely pinned.
[CustomTypeMarshaller(typeof(Span<>), CustomTypeMarshallerKind.LinearCollection, Features = CustomTypeMarshallerFeatures.UnmanagedResources | CustomTypeMarshallerFeatures.CallerAllocatedBuffer | CustomTypeMarshallerFeatures.TwoStageMarshalling, BufferSize = 0)]
public unsafe ref struct DirectSpanMarshaller<T>
where T : unmanaged
{
private T* _allocatedMemory;
private T* _nativeValue;
private Span<T> _data;
public DirectSpanMarshaller(int sizeOfNativeElement)
:this()
{
}
public DirectSpanMarshaller(Span<T> managed, int sizeOfNativeElement)
:this(sizeOfNativeElement)
{
if (managed.Length == 0)
{
return;
}
int spaceToAllocate = managed.Length * Unsafe.SizeOf<T>();
_allocatedMemory = (T*)Marshal.AllocCoTaskMem(spaceToAllocate);
_data = managed;
}
public DirectSpanMarshaller(Span<T> managed, Span<byte> stackSpace, int sizeOfNativeElement)
:this(sizeOfNativeElement)
{
Debug.Assert(stackSpace.IsEmpty);
_data = managed;
}
public ReadOnlySpan<T> GetManagedValuesSource() => _data;
public Span<T> GetManagedValuesDestination(int length) => _data = new Span<T>(_nativeValue, length);
public Span<byte> GetNativeValuesDestination() => _allocatedMemory != null
? new Span<byte>(_allocatedMemory, _data.Length * Unsafe.SizeOf<T>())
: MemoryMarshal.Cast<T, byte>(_data);
public ReadOnlySpan<byte> GetNativeValuesSource(int length) => new ReadOnlySpan<byte>(_nativeValue, length * sizeof(T));
public ref T GetPinnableReference() => ref _data.GetPinnableReference();
public T* ToNativeValue()
{
if (_allocatedMemory != null)
{
return _allocatedMemory;
}
return (T*)Unsafe.AsPointer(ref GetPinnableReference());
}
public void FromNativeValue(T* value)
{
_allocatedMemory = null;
_nativeValue = value;
}
public Span<T> ToManaged()
{
return _data;
}
public void FreeNative()
{
Marshal.FreeCoTaskMem((IntPtr)_allocatedMemory);
}
}
}
......@@ -72,10 +72,6 @@ public partial class Arrays
[LibraryImport(NativeExportsNE_Binary, EntryPoint = "double_values")]
public static partial void DoubleValues([In, Out] IntStructWrapper[] array, int length);
[LibraryImport(NativeExportsNE_Binary, EntryPoint = "and_all_members")]
[return:MarshalAs(UnmanagedType.U1)]
public static partial bool AndAllMembers(BoolStruct_V1[] pArray, int length);
[LibraryImport(NativeExportsNE_Binary, EntryPoint = "and_bool_struct_array")]
[return: MarshalAs(UnmanagedType.U1)]
public static partial bool AndAllMembers(BoolStruct[] pArray, int length);
......
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using SharedTypes;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Marshalling;
using System.Text;
using Xunit;
namespace LibraryImportGenerator.IntegrationTests
{
partial class NativeExportsNE
{
public partial class Collections
{
public partial class V1
{
[LibraryImport(NativeExportsNE_Binary, EntryPoint = "sum_int_array")]
public static partial int Sum([MarshalUsing(typeof(ListMarshaller_V1<int>))] List<int> values, int numValues);
[LibraryImport(NativeExportsNE_Binary, EntryPoint = "sum_int_array_ref")]
public static partial int SumInArray([MarshalUsing(typeof(ListMarshaller_V1<int>))] in List<int> values, int numValues);
[LibraryImport(NativeExportsNE_Binary, EntryPoint = "duplicate_int_array")]
public static partial void Duplicate([MarshalUsing(typeof(ListMarshaller_V1<int>), CountElementName = "numValues")] ref List<int> values, int numValues);
[LibraryImport(NativeExportsNE_Binary, EntryPoint = "create_range_array")]
[return: MarshalUsing(typeof(ListMarshaller_V1<int>), CountElementName = "numValues")]
public static partial List<int> CreateRange(int start, int end, out int numValues);
[LibraryImport(NativeExportsNE_Binary, EntryPoint = "create_range_array_out")]
public static partial void CreateRange_Out(int start, int end, out int numValues, [MarshalUsing(typeof(ListMarshaller_V1<int>), CountElementName = "numValues")] out List<int> res);
[LibraryImport(NativeExportsNE_Binary, EntryPoint = "sum_string_lengths")]
public static partial int SumStringLengths([MarshalUsing(typeof(ListMarshaller_V1<string>)), MarshalUsing(typeof(Utf16StringMarshaller), ElementIndirectionDepth = 1)] List<string> strArray);
[LibraryImport(NativeExportsNE_Binary, EntryPoint = "sum_string_lengths")]
public static partial int SumStringLengths([MarshalUsing(typeof(Utf16StringMarshaller), ElementIndirectionDepth = 1)] WrappedList_V1<string> strArray);
[LibraryImport(NativeExportsNE_Binary, EntryPoint = "reverse_strings_replace")]
public static partial void ReverseStrings_Ref([MarshalUsing(typeof(ListMarshaller_V1<string>), CountElementName = "numElements"), MarshalUsing(typeof(Utf16StringMarshaller), ElementIndirectionDepth = 1)] ref List<string> strArray, out int numElements);
[LibraryImport(NativeExportsNE_Binary, EntryPoint = "reverse_strings_return")]
[return: MarshalUsing(typeof(ListMarshaller_V1<string>), CountElementName = "numElements"), MarshalUsing(typeof(Utf16StringMarshaller), ElementIndirectionDepth = 1)]
public static partial List<string> ReverseStrings_Return([MarshalUsing(typeof(ListMarshaller_V1<string>), CountElementName = "numElements"), MarshalUsing(typeof(Utf16StringMarshaller), ElementIndirectionDepth = 1)] List<string> strArray, out int numElements);
[LibraryImport(NativeExportsNE_Binary, EntryPoint = "reverse_strings_out")]
public static partial void ReverseStrings_Out(
[MarshalUsing(typeof(ListMarshaller_V1<string>)), MarshalUsing(typeof(Utf16StringMarshaller), ElementIndirectionDepth = 1)] List<string> strArray,
out int numElements,
[MarshalUsing(typeof(ListMarshaller_V1<string>), CountElementName = "numElements"), MarshalUsing(typeof(Utf16StringMarshaller), ElementIndirectionDepth = 1)] out List<string> res);
[LibraryImport(NativeExportsNE_Binary, EntryPoint = "get_long_bytes")]
[return: MarshalUsing(typeof(ListMarshaller_V1<byte>), ConstantElementCount = sizeof(long))]
public static partial List<byte> GetLongBytes(long l);
[LibraryImport(NativeExportsNE_Binary, EntryPoint = "and_all_members")]
[return: MarshalAs(UnmanagedType.U1)]
public static partial bool AndAllMembers([MarshalUsing(typeof(ListMarshaller_V1<BoolStruct_V1>))] List<BoolStruct_V1> pArray, int length);
}
}
}
public class CollectionTests_V1
{
[Fact]
public void BlittableElementColllectionMarshalledToNativeAsExpected()
{
var list = new List<int> { 1, 5, 79, 165, 32, 3 };
Assert.Equal(list.Sum(), NativeExportsNE.Collections.V1.Sum(list, list.Count));
}
[Fact]
public void NullBlittableElementColllectionMarshalledToNativeAsExpected()
{
Assert.Equal(-1, NativeExportsNE.Collections.V1.Sum(null, 0));
}
[Fact]
public void BlittableElementColllectionInParameter()
{
var list = new List<int> { 1, 5, 79, 165, 32, 3 };
Assert.Equal(list.Sum(), NativeExportsNE.Collections.V1.SumInArray(list, list.Count));
}
[Fact]
public void BlittableElementCollectionRefParameter()
{
var list = new List<int> { 1, 5, 79, 165, 32, 3 };
var newList = list;
NativeExportsNE.Collections.V1.Duplicate(ref newList, list.Count);
Assert.Equal((IEnumerable<int>)list, newList);
}
[Fact]
public void BlittableElementCollectionReturnedFromNative()
{
int start = 5;
int end = 20;
IEnumerable<int> expected = Enumerable.Range(start, end - start);
Assert.Equal(expected, NativeExportsNE.Collections.V1.CreateRange(start, end, out _));
List<int> res;
NativeExportsNE.Collections.V1.CreateRange_Out(start, end, out _, out res);
Assert.Equal(expected, res);
}
[Fact]
public void NullBlittableElementCollectionReturnedFromNative()
{
Assert.Null(NativeExportsNE.Collections.V1.CreateRange(1, 0, out _));
List<int> res;
NativeExportsNE.Collections.V1.CreateRange_Out(1, 0, out _, out res);
Assert.Null(res);
}
private static List<string> GetStringList()
{
return new()
{
"ABCdef 123$%^",
"🍜 !! 🍜 !!",
"🌲 木 🔥 火 🌾 土 🛡 金 🌊 水" ,
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed vitae posuere mauris, sed ultrices leo. Suspendisse potenti. Mauris enim enim, blandit tincidunt consequat in, varius sit amet neque. Morbi eget porttitor ex. Duis mattis aliquet ante quis imperdiet. Duis sit.",
string.Empty,
null
};
}
[Fact]
public void ByValueCollectionWithNonBlittableElements()
{
var strings = GetStringList();
Assert.Equal(strings.Sum(str => str?.Length ?? 0), NativeExportsNE.Collections.V1.SumStringLengths(strings));
}
[Fact]
public void ByValueNullCollectionWithNonBlittableElements()
{
Assert.Equal(0, NativeExportsNE.Collections.V1.SumStringLengths(null));
}
[Fact]
public void ByValueCollectionWithNonBlittableElements_WithDefaultMarshalling()
{
var strings = new WrappedList_V1<string>(GetStringList());
Assert.Equal(strings.Wrapped.Sum(str => str?.Length ?? 0), NativeExportsNE.Collections.V1.SumStringLengths(strings));
}
[Fact]
public void ByRefCollectionWithNonBlittableElements()
{
var strings = GetStringList();
var expectedStrings = strings.Select(s => ReverseChars(s)).ToList();
NativeExportsNE.Collections.V1.ReverseStrings_Ref(ref strings, out _);
Assert.Equal((IEnumerable<string>)expectedStrings, strings);
}
[Fact]
public void ReturnCollectionWithNonBlittableElements()
{
var strings = GetStringList();
var expectedStrings = strings.Select(s => ReverseChars(s)).ToList();
Assert.Equal(expectedStrings, NativeExportsNE.Collections.V1.ReverseStrings_Return(strings, out _));
List<string> res;
NativeExportsNE.Collections.V1.ReverseStrings_Out(strings, out _, out res);
Assert.Equal(expectedStrings, res);
}
[Fact]
public void ByRefNullCollectionWithNonBlittableElements()
{
List<string> strings = null;
NativeExportsNE.Collections.V1.ReverseStrings_Ref(ref strings, out _);
Assert.Null(strings);
}
[Fact]
public void ReturnNullCollectionWithNonBlittableElements()
{
List<string> strings = null;
Assert.Null(NativeExportsNE.Collections.V1.ReverseStrings_Return(strings, out _));
List<string> res;
NativeExportsNE.Collections.V1.ReverseStrings_Out(strings, out _, out res);
Assert.Null(res);
}
[Fact]
public void ConstantSizeCollection()
{
var longVal = 0x12345678ABCDEF10L;
Assert.Equal(longVal, MemoryMarshal.Read<long>(CollectionsMarshal.AsSpan(NativeExportsNE.Collections.V1.GetLongBytes(longVal))));
}
[Theory]
[InlineData(true)]
[InlineData(false)]
public void CollectionWithSimpleNonBlittableTypeMarshalling(bool result)
{
var boolValues = new List<BoolStruct_V1>
{
new BoolStruct_V1
{
b1 = true,
b2 = true,
b3 = true,
},
new BoolStruct_V1
{
b1 = true,
b2 = true,
b3 = true,
},
new BoolStruct_V1
{
b1 = true,
b2 = true,
b3 = result,
},
};
Assert.Equal(result, NativeExportsNE.Collections.V1.AndAllMembers(boolValues, boolValues.Count));
}
private static string ReverseChars(string value)
{
if (value == null)
return null;
var chars = value.ToCharArray();
Array.Reverse(chars);
return new string(chars);
}
}
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Marshalling;
using System.Text;
using SharedTypes;
using Xunit;
namespace LibraryImportGenerator.IntegrationTests
{
partial class NativeExportsNE
{
internal partial class V1
{
[LibraryImport(NativeExportsNE_Binary, EntryPoint = "stringcontainer_deepduplicate")]
public static partial void DeepDuplicateStrings(StringContainer_V1 strings, out StringContainer_V1 pStringsOut);
[LibraryImport(NativeExportsNE_Binary, EntryPoint = "stringcontainer_reverse_strings")]
public static partial void ReverseStrings(ref StringContainer_V1 strings);
[LibraryImport(NativeExportsNE_Binary, EntryPoint = "get_long_bytes_as_double")]
public static partial double GetLongBytesAsDouble([MarshalUsing(typeof(DoubleToLongMarshaller_V1))] double d);
[LibraryImport(NativeExportsNE_Binary, EntryPoint = "negate_bools")]
public static partial void NegateBools(
BoolStruct_V1 boolStruct,
out BoolStruct_V1 pBoolStructOut);
[LibraryImport(NativeExportsNE_Binary, EntryPoint = "and_bools_ref")]
[return: MarshalAs(UnmanagedType.U1)]
public static partial bool AndBoolsRef(in BoolStruct_V1 boolStruct);
[LibraryImport(NativeExportsNE_Binary, EntryPoint = "double_int_ref")]
public static partial IntWrapper_V1 DoubleIntRef(IntWrapper_V1 pInt);
}
}
public class CustomMarshallingTests_V1
{
[Fact]
public void NonBlittableStructWithFree()
{
var stringContainer = new StringContainer_V1
{
str1 = "Foo",
str2 = "Bar"
};
NativeExportsNE.V1.DeepDuplicateStrings(stringContainer, out var stringContainer2);
Assert.Equal(stringContainer, stringContainer2);
}
[Fact]
public void MarshalUsing()
{
double d = 1234.56789;
Assert.Equal(d, NativeExportsNE.V1.GetLongBytesAsDouble(d));
}
[Fact]
public void NonBlittableStructWithoutAllocation()
{
var boolStruct = new BoolStruct_V1
{
b1 = true,
b2 = false,
b3 = true
};
NativeExportsNE.V1.NegateBools(boolStruct, out BoolStruct_V1 boolStructNegated);
Assert.Equal(!boolStruct.b1, boolStructNegated.b1);
Assert.Equal(!boolStruct.b2, boolStructNegated.b2);
Assert.Equal(!boolStruct.b3, boolStructNegated.b3);
}
[Fact]
public void GetPinnableReferenceMarshalling()
{
int originalValue = 42;
var wrapper = new IntWrapper_V1 { i = originalValue };
var retVal = NativeExportsNE.V1.DoubleIntRef(wrapper);
Assert.Equal(originalValue * 2, wrapper.i);
Assert.Equal(originalValue * 2, retVal.i);
}
[Fact]
public void NonBlittableStructRef()
{
var stringContainer = new StringContainer_V1
{
str1 = "Foo",
str2 = "Bar"
};
var expected = new StringContainer_V1
{
str1 = ReverseUTF8Bytes(stringContainer.str1),
str2 = ReverseUTF8Bytes(stringContainer.str2)
};
var stringContainerCopy = stringContainer;
NativeExportsNE.V1.ReverseStrings(ref stringContainerCopy);
Assert.Equal(expected, stringContainerCopy);
}
[Theory]
[InlineData(true, true, true)]
[InlineData(true, true, false)]
[InlineData(true, false, true)]
[InlineData(true, false, false)]
[InlineData(false, true, true)]
[InlineData(false, true, false)]
[InlineData(false, false, true)]
[InlineData(false, false, false)]
public void NonBlittableStructIn(bool b1, bool b2, bool b3)
{
var container = new BoolStruct_V1
{
b1 = b1,
b2 = b2,
b3 = b3
};
Assert.Equal(b1 && b2 && b3, NativeExportsNE.V1.AndBoolsRef(container));
}
private static string ReverseChars(string value)
{
if (value == null)
return null;
var chars = value.ToCharArray();
Array.Reverse(chars);
return new string(chars);
}
private static string ReverseUTF8Bytes(string value)
{
if (value == null)
return null;
byte[] bytes = Encoding.UTF8.GetBytes(value);
Array.Reverse(bytes);
return Encoding.UTF8.GetString(bytes);
}
}
}
......@@ -156,18 +156,6 @@ public void NonBlittableStructWithoutAllocation()
Assert.Equal(!boolStruct.b3, boolStructNegated.b3);
}
[Fact]
public void ManagedTypeGetPinnableReferenceMarshalling()
{
int originalValue = 42;
var wrapper = new IntWrapper { i = originalValue };
var retVal = NativeExportsNE.Stateless.DoubleIntRef(wrapper);
Assert.Equal(originalValue * 2, wrapper.i);
Assert.Equal(originalValue * 2, retVal.i);
}
[Fact]
public void MarshallerStaticGetPinnableReferenceMarshalling()
{
......
......@@ -9,21 +9,19 @@
namespace LibraryImportGenerator.IntegrationTests
{
[CustomTypeMarshaller(typeof(int))]
public struct SetLastErrorMarshaller
[CustomMarshaller(typeof(int), MarshalMode.Default, typeof(SetLastErrorMarshaller))]
public static class SetLastErrorMarshaller
{
public int val;
public SetLastErrorMarshaller(int i)
public static int ConvertToUnmanaged(int i)
{
val = i;
return i;
}
public int ToManaged()
public static int ConvertToManaged(int i )
{
// Explicity set the last error to something else on unmarshalling
Marshal.SetLastPInvokeError(val * 2);
return val;
Marshal.SetLastPInvokeError(i * 2);
return i;
}
}
......
......@@ -19,37 +19,30 @@ partial class NativeExportsNE
public partial class Span
{
[LibraryImport(NativeExportsNE_Binary, EntryPoint = "sum_int_array")]
public static partial int Sum([MarshalUsing(typeof(SpanMarshaller<int>))] Span<int> values, int numValues);
[LibraryImport(NativeExportsNE_Binary, EntryPoint = "sum_int_array")]
public static partial int SumNeverNull([MarshalUsing(typeof(NeverNullSpanMarshaller<int>))] Span<int> values, int numValues);
public static partial int Sum(Span<int> values, int numValues);
[LibraryImport(NativeExportsNE_Binary, EntryPoint = "sum_int_array")]
public static partial int SumNeverNull([MarshalUsing(typeof(NeverNullReadOnlySpanMarshaller<int>))] ReadOnlySpan<int> values, int numValues);
public static partial int Sum(ReadOnlySpan<int> values, int numValues);
[LibraryImport(NativeExportsNE_Binary, EntryPoint = "sum_int_array_ref")]
public static partial int SumInArray([MarshalUsing(typeof(SpanMarshaller<int>))] in Span<int> values, int numValues);
public static partial int SumInArray(in Span<int> values, int numValues);
[LibraryImport(NativeExportsNE_Binary, EntryPoint = "duplicate_int_array")]
public static partial void Duplicate([MarshalUsing(typeof(SpanMarshaller<int>), CountElementName = "numValues")] ref Span<int> values, int numValues);
[LibraryImport(NativeExportsNE_Binary, EntryPoint = "duplicate_int_array")]
public static partial void DuplicateRaw([MarshalUsing(typeof(DirectSpanMarshaller<int>), CountElementName = "numValues")] ref Span<int> values, int numValues);
public static partial void Duplicate([MarshalUsing(CountElementName = "numValues")] ref Span<int> values, int numValues);
[LibraryImport(NativeExportsNE_Binary, EntryPoint = "create_range_array")]
[return: MarshalUsing(typeof(SpanMarshaller<int>), CountElementName = "numValues")]
[return: MarshalUsing(CountElementName = "numValues")]
public static partial Span<int> CreateRange(int start, int end, out int numValues);
[LibraryImport(NativeExportsNE_Binary, EntryPoint = "create_range_array_out")]
public static partial void CreateRange_Out(int start, int end, out int numValues, [MarshalUsing(typeof(SpanMarshaller<int>), CountElementName = "numValues")] out Span<int> res);
public static partial void CreateRange_Out(int start, int end, out int numValues, [MarshalUsing(CountElementName = "numValues")] out Span<int> res);
[LibraryImport(NativeExportsNE_Binary, EntryPoint = "get_long_bytes")]
[return: MarshalUsing(typeof(SpanMarshaller<byte>), ConstantElementCount = sizeof(long))]
[return: MarshalUsing(ConstantElementCount = sizeof(long))]
public static partial Span<byte> GetLongBytes(long l);
[LibraryImport(NativeExportsNE_Binary, EntryPoint = "and_all_members")]
[return: MarshalAs(UnmanagedType.U1)]
public static partial bool AndAllMembers([MarshalUsing(typeof(SpanMarshaller<BoolStruct_V1>))] Span<BoolStruct_V1> pArray, int length);
public static partial bool AndAllMembers(Span<BoolStruct> pArray, int length);
}
}
......@@ -69,15 +62,17 @@ public void DefaultBlittableElementSpanMarshalledToNativeAsExpected()
}
[Fact]
public void NeverNullSpanMarshallerMarshalsDefaultAsNonNull()
public void ZeroLengthSpanMarshalsAsNonNull()
{
Assert.Equal(0, NativeExportsNE.Span.SumNeverNull(Span<int>.Empty, 0));
Span<int> list = new int[0];
Assert.Equal(0, NativeExportsNE.Span.Sum(list, list.Length));
}
[Fact]
public void NeverNullReadOnlySpanMarshallerMarshalsDefaultAsNonNull()
public void ZeroLengthReadOnlySpanMarshalsAsNonNull()
{
Assert.Equal(0, NativeExportsNE.Span.SumNeverNull(ReadOnlySpan<int>.Empty, 0));
ReadOnlySpan<int> list = new int[0];
Assert.Equal(0, NativeExportsNE.Span.Sum(list, list.Length));
}
[Fact]
......@@ -96,16 +91,6 @@ public void BlittableElementSpanRefParameter()
Assert.Equal((IEnumerable<int>)list, newSpan.ToArray());
}
[Fact]
public unsafe void DirectSpanMarshaller()
{
var list = new int[] { 1, 5, 79, 165, 32, 3 };
Span<int> newSpan = list;
NativeExportsNE.Span.DuplicateRaw(ref newSpan, list.Length);
Assert.Equal((IEnumerable<int>)list, newSpan.ToArray());
Marshal.FreeCoTaskMem((IntPtr)Unsafe.AsPointer(ref newSpan.GetPinnableReference()));
}
[Fact]
public void BlittableElementSpanReturnedFromNative()
{
......@@ -135,21 +120,21 @@ public void NullBlittableElementSpanReturnedFromNative()
[InlineData(false)]
public void SpanWithSimpleNonBlittableTypeMarshalling(bool result)
{
var boolValues = new BoolStruct_V1[]
var boolValues = new BoolStruct[]
{
new BoolStruct_V1
new BoolStruct
{
b1 = true,
b2 = true,
b3 = true,
},
new BoolStruct_V1
new BoolStruct
{
b1 = true,
b2 = true,
b3 = true,
},
new BoolStruct_V1
new BoolStruct
{
b1 = true,
b2 = true,
......
......@@ -41,17 +41,21 @@ partial class Foo
public static partial void {|CS8795:PInvoke|}(S {|#0:s|});
}
[NativeMarshalling(typeof(Native))]
[NativeMarshalling(typeof(Marshaller))]
struct S
{
}
[CustomTypeMarshaller(typeof(S))]
struct Native
{
public Native(S s) {}
}
[CustomMarshaller(typeof(S), MarshalMode.Default, typeof(Marshaller))]
static class Marshaller
{
public static Native ConvertToUnmanaged(S s) => default;
public S ToManaged() => new S();
public static S ConvertToManaged(Native n) => default;
}
";
var expectedPropertiesFile = "[assembly: System.Runtime.CompilerServices.DisableRuntimeMarshalling]" + Environment.NewLine;
......@@ -72,17 +76,21 @@ partial class Foo
public static partial void {|CS8795:PInvoke|}(S {|#0:s|});
}
[NativeMarshalling(typeof(Native))]
[NativeMarshalling(typeof(Marshaller))]
struct S
{
}
[CustomTypeMarshaller(typeof(S))]
struct Native
{
public Native(S s) {}
}
[CustomMarshaller(typeof(S), MarshalMode.Default, typeof(Marshaller))]
static class Marshaller
{
public static Native ConvertToUnmanaged(S s) => default;
public S ToManaged() => new S();
public static S ConvertToManaged(Native n) => default;
}
";
var propertiesFile = @"
......
......@@ -27,16 +27,21 @@ partial class C
public static partial S Method();
}
[NativeMarshalling(typeof(Native))]
[NativeMarshalling(typeof(Marshaller))]
struct S
{
}
[CustomTypeMarshaller(typeof(S))]
struct Native
{
public Native(S s) { }
public S ToManaged() { return default; }
}
[CustomMarshaller(typeof(S), MarshalMode.Default, typeof(Marshaller))]
static class Marshaller
{
public static Native ConvertToUnmanaged(S s) => default;
public static S ConvertToManaged(Native n) => default;
}";
Compilation comp = await TestUtils.CreateCompilation(source);
......@@ -80,16 +85,21 @@ partial class C
public static partial S Method();
}
[NativeMarshalling(typeof(Native))]
[NativeMarshalling(typeof(Marshaller))]
struct S
{
}
[CustomTypeMarshaller(typeof(S))]
struct Native
{
public Native(S s) { }
public S ToManaged() { return default; }
}
[CustomMarshaller(typeof(S), MarshalMode.Default, typeof(Marshaller))]
static class Marshaller
{
public static Native ConvertToUnmanaged(S s) => default;
public static S ConvertToManaged(Native n) => default;
}";
Compilation comp = await TestUtils.CreateCompilation(source);
......@@ -173,16 +183,21 @@ partial class C
public static partial S Method();
}
[NativeMarshalling(typeof(Native))]
[NativeMarshalling(typeof(Marshaller))]
struct S
{
}
[CustomTypeMarshaller(typeof(S))]
struct Native
{
public Native(S s) { }
public S ToManaged() { return default; }
}
[CustomMarshaller(typeof(S), MarshalMode.Default, typeof(Marshaller))]
static class Marshaller
{
public static Native ConvertToUnmanaged(S s) => default;
public static S ConvertToManaged(Native n) => default;
}";
Compilation comp = await TestUtils.CreateCompilation(source);
......@@ -207,16 +222,21 @@ partial class C
public static partial S Method();
}
[NativeMarshalling(typeof(Native))]
[NativeMarshalling(typeof(Marshaller))]
struct S
{
}
[CustomTypeMarshaller(typeof(S))]
struct Native
{
public Native(S s) { }
public S ToManaged() { return default; }
}
[CustomMarshaller(typeof(S), MarshalMode.Default, typeof(Marshaller))]
static class Marshaller
{
public static Native ConvertToUnmanaged(S s) => default;
public static S ConvertToManaged(Native n) => default;
}";
Compilation comp = await TestUtils.CreateCompilation(source);
......@@ -241,16 +261,21 @@ partial class C
public static partial S Method();
}
[NativeMarshalling(typeof(Native))]
[NativeMarshalling(typeof(Marshaller))]
struct S
{
}
[CustomTypeMarshaller(typeof(S))]
struct Native
{
public Native(S s) { }
public S ToManaged() { return default; }
}
[CustomMarshaller(typeof(S), MarshalMode.Default, typeof(Marshaller))]
static class Marshaller
{
public static Native ConvertToUnmanaged(S s) => default;
public static S ConvertToManaged(Native n) => default;
}";
Compilation comp = await TestUtils.CreateCompilation(source);
......
......@@ -107,11 +107,6 @@ public static IEnumerable<object[]> CodeSnippetsToCompile()
yield return new object[] { ID(), CodeSnippets.CustomStructMarshalling.Stateful.ManagedToNativeOnlyReturnValue, 1, 0 };
yield return new object[] { ID(), CodeSnippets.CustomStructMarshalling.Stateful.NativeToManagedOnlyInParameter, 1, 0 };
yield return new object[] { ID(), CodeSnippets.CustomStructMarshalling.Stateful.StackallocOnlyRefParameter, 1, 0 };
yield return new object[] { ID(), CodeSnippets.CustomStructMarshalling_V1.TwoStageRefReturn, 3, 0 };
yield return new object[] { ID(), CodeSnippets.CustomStructMarshalling_V1.ManagedToNativeOnlyOutParameter, 1, 0 };
yield return new object[] { ID(), CodeSnippets.CustomStructMarshalling_V1.ManagedToNativeOnlyReturnValue, 1, 0 };
yield return new object[] { ID(), CodeSnippets.CustomStructMarshalling_V1.NativeToManagedOnlyInParameter, 1, 0 };
yield return new object[] { ID(), CodeSnippets.CustomStructMarshalling_V1.StackallocOnlyRefParameter, 1, 0 };
// Abstract SafeHandle type by reference
yield return new object[] { ID(), CodeSnippets.BasicParameterWithByRefModifier("ref", "System.Runtime.InteropServices.SafeHandle"), 1, 0 };
......@@ -124,13 +119,10 @@ public static IEnumerable<object[]> CodeSnippetsToCompile()
// Generic collection marshaller has different arity than collection.
yield return new object[] { ID(), CodeSnippets.CustomCollectionMarshalling.Stateless.GenericCollectionMarshallingArityMismatch, 2, 0 };
yield return new object[] { ID(), CodeSnippets.CustomCollectionMarshalling_V1.GenericCollectionMarshallingArityMismatch, 2, 0 };
yield return new object[] { ID(), CodeSnippets.MarshalAsAndMarshalUsingOnReturnValue, 2, 0 };
yield return new object[] { ID(), CodeSnippets.CustomCollectionMarshalling.Stateless.CustomElementMarshallingDuplicateElementIndirectionDepth, 2, 0 };
yield return new object[] { ID(), CodeSnippets.CustomCollectionMarshalling.Stateless.CustomElementMarshallingUnusedElementIndirectionDepth, 1, 0 };
yield return new object[] { ID(), CodeSnippets.CustomCollectionMarshalling_V1.GenericCollectionWithCustomElementMarshallingDuplicateElementIndirectionDepth, 2, 0 };
yield return new object[] { ID(), CodeSnippets.CustomCollectionMarshalling_V1.GenericCollectionWithCustomElementMarshallingUnusedElementIndirectionDepth, 1, 0 };
yield return new object[] { ID(), CodeSnippets.RecursiveCountElementNameOnReturnValue, 2, 0 };
yield return new object[] { ID(), CodeSnippets.RecursiveCountElementNameOnParameter, 2, 0 };
yield return new object[] { ID(), CodeSnippets.MutuallyRecursiveCountElementNameOnParameter, 4, 0 };
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册