Skip to content

Introduce mechanism to indicate arguments are to be marshalled as native varargs #48796

Open
@AaronRobinsonMSFT

Description

@AaronRobinsonMSFT

Background and Motivation

Native varargs are a complicated interop scenario to support. At present, native varargs are only supported on the Windows platform through the undocumented __arglist keyword. Supporting varargs naturally in a P/Invoke scenario would be difficult from the C# language. However, it is possible to compromise by permitting support for the call with a fully specified DllImport signature and a hint from the user.

User scenario: #48752

Proposed API

namespace System.Runtime.InteropServices
{
+    [AttributeUsage(AttributeTargets.Method)]
+    public class NativeVarargsAttribute : Attribute
+    {
+        public NativeVarargsAttribute() { VarargBeginIndex = 0; }
+
+        /// <summary>
+        /// Zero-based index of the first variable argument.
+        /// </summary>
+        public int VarargBeginIndex;
+    }
}

Usage Examples

Consider the following native export with varargs:

void Varargs(int n, ...);

The following P/Invoke declarations would enable users to call and properly forward the arguments in a supported multi-platform manner.

[NativeVarargsAttribute(VarargBeginIndex = 1)]
[DllImport(@"NativeLibrary.dll", EntryPoint = "Varargs")]
static extern void Varargs0(int n);

[NativeVarargsAttribute(VarargBeginIndex = 1)]
[DllImport(@"NativeLibrary.dll", EntryPoint = "Varargs")]
static extern void Varargs1(int n, int a);

[NativeVarargsAttribute(VarargBeginIndex = 1)]
[DllImport(@"NativeLibrary.dll", EntryPoint = "Varargs")]
static extern void Varargs2(int n, int a, int b);

Alternative designs

Encode the information in the CallingConvention enum. This approach does remove the overhead of attribute reading, but does miss the added data of knowing where the varargs start - at present doesn't appear to be needed. This approach also impacts existing metadata tooling (for example, ILDasm, ILAsm, and ILSpy). See #48796 (comment).

public enum CallingConvention
{
+    VarArg = 6
}

Current state

Without this feature, calling functions with native varargs isn't possible on a non-Windows platforms. The proposed workaround is to create native shim libraries and instead P/Invoke into them. Continuing the example above, the shim library would export the following:

extern void Varargs(int n, ...);

void Varargs0(int n)
{
    Varargs(n);
}
void Varargs1(int n, int a)
{
    Varargs(n, a);
}
void Varargs2(int n, int a, int b)
{
    Varargs(n, a, b);
}

References

Support on Windows: dotnet/coreclr#18373
JIT details: #10478

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    Status

    No status

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions