diff --git a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CodeSnippets.cs b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CodeSnippets.cs index 56eb1d52dbe1ee..3228bfc44ed64f 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CodeSnippets.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CodeSnippets.cs @@ -1438,5 +1438,93 @@ public Native(S s) public static string TypeUsage(string attr) => MarshalUsingParametersAndModifiers("S", "Marshaller", attr); } + + public const string ImproperCollectionWithMarshalUsingOnElements = """ + using System; + using System.Collections.Generic; + using System.Runtime.InteropServices; + using System.Runtime.InteropServices.Marshalling; + + class MyList + { + } + + class NotMyList {} + + class Example + { + public int Value; + } + + internal static partial class PInvoke + { + [LibraryImport("NativeLibrary", EntryPoint = "ProcessExample")] + public static partial int ProcessExample([MarshalUsing(typeof(ListMarshaller<,>), CountElementName = nameof(exampleCount)), MarshalUsing(typeof(ExampleMarshaller), ElementIndirectionDepth = 1)] MyList examples, ref int exampleCount); + } + + [ContiguousCollectionMarshaller] + [CustomMarshaller(typeof(MyList<>), MarshalMode.Default, typeof(ListMarshaller<,>.Marshaller))] + public unsafe static class ListMarshaller where TUnmanagedElement : unmanaged + { + public static class Marshaller + { + public static byte* AllocateContainerForUnmanagedElements(NotMyList managed, out int numElements) + { + numElements = default; + return default; + } + + public static ReadOnlySpan GetManagedValuesSource(NotMyList managed) + => default; + + public static Span GetUnmanagedValuesDestination(byte* unmanaged, int numElements) + => new Span((TUnmanagedElement*)unmanaged, numElements); + + public static List AllocateContainerForManagedElements(byte* unmanaged, int length) + => new List(length); + + public static Span GetManagedValuesDestination(NotMyList managed) + => default; + + public static ReadOnlySpan GetUnmanagedValuesSource(byte* nativeValue, int numElements) + => new ReadOnlySpan((TUnmanagedElement*)nativeValue, numElements); + + public static void Free(byte* unmanaged) + => NativeMemory.Free(unmanaged); + } + } + + [CustomMarshaller(typeof(Example), MarshalMode.ManagedToUnmanagedIn, typeof(ManagedToNative))] + [CustomMarshaller(typeof(Example), MarshalMode.ManagedToUnmanagedOut, typeof(ManagedToNativeOutFinally))] + static class ExampleMarshaller + { + public static class ManagedToNativeOutFinally + { + public static Example ConvertToManagedFinally(int managed) + { + return default; + } + + public static void Free(int unmanaged) + { + } + } + + public struct ManagedToNative + { + Example managed; + + public static int BufferSize => sizeof(int); + + public void FromManaged(Example managed, Span buffer) => this.managed = managed; + + public int ToUnmanaged() => managed.Value; + + public void OnInvoked() { } + + public void Free() { } + } + } + """; } } diff --git a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CompileFails.cs b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CompileFails.cs index 605cd6b80386c5..952f8ef2890c81 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CompileFails.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CompileFails.cs @@ -57,6 +57,9 @@ public static IEnumerable CodeSnippetsToCompile() // Not LibraryImportAttribute yield return new object[] { ID(), CodeSnippets.UserDefinedPrefixedAttributes, Array.Empty() }; + // Bug: https://github.com/dotnet/runtime/issues/117448 + // yield return new[] { ID(), CodeSnippets.ImproperCollectionWithMarshalUsingOnElements }; + // No explicit marshalling for char or string yield return new object[] { ID(), CodeSnippets.BasicParametersAndModifiers(), new[] {