Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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<T>
{
}

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<Example> examples, ref int exampleCount);
}

[ContiguousCollectionMarshaller]
[CustomMarshaller(typeof(MyList<>), MarshalMode.Default, typeof(ListMarshaller<,>.Marshaller))]
public unsafe static class ListMarshaller<T, TUnmanagedElement> where TUnmanagedElement : unmanaged
{
public static class Marshaller
{
public static byte* AllocateContainerForUnmanagedElements(NotMyList managed, out int numElements)
{
numElements = default;
return default;
}

public static ReadOnlySpan<T> GetManagedValuesSource(NotMyList managed)
=> default;

public static Span<TUnmanagedElement> GetUnmanagedValuesDestination(byte* unmanaged, int numElements)
=> new Span<TUnmanagedElement>((TUnmanagedElement*)unmanaged, numElements);

public static List<T> AllocateContainerForManagedElements(byte* unmanaged, int length)
=> new List<T>(length);

public static Span<T> GetManagedValuesDestination(NotMyList managed)
=> default;

public static ReadOnlySpan<TUnmanagedElement> GetUnmanagedValuesSource(byte* nativeValue, int numElements)
=> new ReadOnlySpan<TUnmanagedElement>((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<int> buffer) => this.managed = managed;

public int ToUnmanaged() => managed.Value;

public void OnInvoked() { }

public void Free() { }
}
}
""";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ public static IEnumerable<object[]> CodeSnippetsToCompile()
// Not LibraryImportAttribute
yield return new object[] { ID(), CodeSnippets.UserDefinedPrefixedAttributes, Array.Empty<DiagnosticResult>() };

// 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<char>(), new[]
{
Expand Down
Loading