Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Array marshalling support #272

Merged
merged 20 commits into from
Nov 3, 2020

Conversation

jkoritzinsky
Copy link
Member

Implement support for marshalling blittable and non-blittable arrays, with the small size stack allocation optimization for arrays under 256 bytes.

Includes support for both SizeParamIndex and SizeConst with the same semantics as the runtime.

Example codegen:

Pass int array by value:

namespace DllImportGenerator.IntegrationTests
{
    partial class NativeExportsNE
    {
        public partial class Arrays
        {
            public static partial int Sum(int[] values, int numValues)
            {
                unsafe
                {
                    int __retVal;
                    //
                    // Invoke
                    //
                    fixed (int *__values_gen_native = &System.Runtime.InteropServices.MemoryMarshal.GetArrayDataReference(values))
                    {
                        __retVal = Sum__PInvoke__(__values_gen_native, numValues);
                    }

                    return __retVal;
                }
            }

            [System.Runtime.InteropServices.DllImportAttribute("NativeExportsNE", EntryPoint = "sum_int_array")]
            extern private static unsafe int Sum__PInvoke__(int *values, int numValues);
        }
    }
}

Pass int array by reference:

namespace DllImportGenerator.IntegrationTests
{
    partial class NativeExportsNE
    {
        public partial class Arrays
        {
            public static partial void Duplicate(ref int[] values, int numValues)
            {
                unsafe
                {
                    //
                    // Setup
                    //
                    int *__values_gen_native;
                    //
                    // Marshal
                    //
                    int values__bytelen = sizeof(int) * values.Length;
                    __values_gen_native = (int *)System.Runtime.InteropServices.Marshal.AllocCoTaskMem(values__bytelen);
                    values.AsSpan().CopyTo(new System.Span<int>(__values_gen_native, values.Length));
                    //
                    // Invoke
                    //
                    Duplicate__PInvoke__(&__values_gen_native, numValues);
                    //
                    // Unmarshal
                    //
                    if (__values_gen_native != null)
                    {
                        values = new int[checked((int)numValues)];
                        new System.Span<int>(__values_gen_native, values.Length).CopyTo(values);
                    }
                    else
                        values = null;
                    //
                    // Cleanup
                    //
                    System.Runtime.InteropServices.Marshal.FreeCoTaskMem((System.IntPtr)__values_gen_native);
                }
            }

            [System.Runtime.InteropServices.DllImportAttribute("NativeExportsNE", EntryPoint = "duplicate_int_array")]
            extern private static unsafe void Duplicate__PInvoke__(int **values, int numValues);
        }
    }
}

Pass non-blittable array (string array) by reference:

namespace DllImportGenerator.IntegrationTests
{
    partial class NativeExportsNE
    {
        public partial class Arrays
        {
            public static partial void ReverseStrings(ref string[] strArray, out int numElements)
            {
                unsafe
                {
                    numElements = default;
                    //
                    // Setup
                    //
                    ushort **__strArray_gen_native;
                    //
                    // Marshal
                    //
                    int strArray__bytelen = sizeof(ushort *) * strArray.Length;
                    __strArray_gen_native = (ushort **)System.Runtime.InteropServices.Marshal.AllocCoTaskMem(strArray__bytelen);
                    for (int i = 0; i < strArray.Length; ++i)
                    {
                        __strArray_gen_native[i] = (ushort *)System.Runtime.InteropServices.Marshal.StringToCoTaskMemUni(strArray[i]);
                    }

                    //
                    // Invoke
                    //
                    fixed (int *__numElements_gen_native = &numElements)
                    {
                        ReverseStrings__PInvoke__(&__strArray_gen_native, __numElements_gen_native);
                    }

                    //
                    // Unmarshal
                    //
                    if (__strArray_gen_native != null)
                    {
                        strArray = new string[checked((int)numElements)];
                        for (int i = 0; i < strArray.Length; ++i)
                        {
                            strArray[i] = __strArray_gen_native[i] == null ? null : new string ((char *)__strArray_gen_native[i]);
                        }
                    }
                    else
                        strArray = null;
                    //
                    // Cleanup
                    //
                    for (int i = 0; i < strArray.Length; ++i)
                    {
                        System.Runtime.InteropServices.Marshal.FreeCoTaskMem((System.IntPtr)__strArray_gen_native[i]);
                    }

                    System.Runtime.InteropServices.Marshal.FreeCoTaskMem((System.IntPtr)__strArray_gen_native);
                }
            }

            [System.Runtime.InteropServices.DllImportAttribute("NativeExportsNE", EntryPoint = "reverse_strings")]
            extern private static unsafe void ReverseStrings__PInvoke__(ushort ***strArray, int *numElements);
        }
    }
}

Using SizeConst:

namespace DllImportGenerator.IntegrationTests
{
    partial class NativeExportsNE
    {
        public partial class Arrays
        {
            public static partial byte[] GetLongBytes(long l)
            {
                unsafe
                {
                    byte[] __retVal;
                    //
                    // Setup
                    //
                    byte *__retVal_gen_native;
                    //
                    // Invoke
                    //
                    __retVal_gen_native = GetLongBytes__PInvoke__(l);
                    //
                    // Unmarshal
                    //
                    if (__retVal_gen_native != null)
                    {
                        __retVal = new byte[8];
                        new System.Span<byte>(__retVal_gen_native, __retVal.Length).CopyTo(__retVal);
                    }
                    else
                        __retVal = null;
                    return __retVal;
                }
            }

            [System.Runtime.InteropServices.DllImportAttribute("NativeExportsNE", EntryPoint = "get_long_bytes")]
            extern private static unsafe byte *GetLongBytes__PInvoke__(long l);
        }
    }
}

@jkoritzinsky jkoritzinsky added the area-DllImportGenerator Source Generated stubs for P/Invokes in C# label Oct 26, 2020
@jkoritzinsky
Copy link
Member Author

Any more feedback on this PR?


protected override StatementSyntax GenerateStackallocOnlyValueMarshalling(TypePositionInfo info, StubCodeContext context, SyntaxToken byteLengthIdentifier, SyntaxToken stackAllocPtrIdentifier)
{
return EmptyStatement();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will this actually show up as an empty statement (;) in the generated code?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes it will.

@jkoritzinsky jkoritzinsky merged commit bfd398c into dotnet:feature/DllImportGenerator Nov 3, 2020
@jkoritzinsky jkoritzinsky deleted the arrays branch November 3, 2020 00:42
jkoritzinsky added a commit to jkoritzinsky/runtime that referenced this pull request Sep 20, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-DllImportGenerator Source Generated stubs for P/Invokes in C#
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants