Skip to content

[API Proposal]: UnmanagedCallersOnlyAttribute.AssociatedType #127634

@MichalStrehovsky

Description

@MichalStrehovsky

Background and motivation

UnmanagedCallersOnlyAttribute has an EntryPoint property that specifies the method should be available externally under the specified symbolic names. Native AOT and tools like DNNE use this to generate the exports.

This export is unconditonal. If a method in the assembly has this attribute, and the assembly is looked at by the generating tool, the export will be generated and the implementation rooted.

This can be problematic for pregenerated interop code. For example in interop on Apple platforms, the user can specify that their class should be available from native code:

public class MyObject : NSObject
{
    [Export("myMethod")]
    void MyMethod() {}
}

The interop code will generate a native-callable proxy behind the scenes:

class MyObjectProxy
{
    [UnmanagedCallersOnly(EntryPoint = "MyObjectProxy_MyMethod"]
    static void MyMethod(IntPtr handle)
    {
        var obj = (MyObject)Runtime.GetNSObject(handle);
        obj.MyMethod();
    }
}

The proxy entrypoint is generated with a specified EntryPoint because further native glue code will refer to it.

The proxy entrypoint is however not needed if the MyObject class was never used in the program.

#127575 was asking for a mechanism that would allow specifying a list of EntryPoints to generate. However creating such list requires analyzing the program before native compilation or trimming happens. It would be preferable to connect trimming with the UnmanagedCallersOnly EntryPoint generation logic so that we don't have to effectively run trimming twice.

The subsequent downstream tools can then observe entrypoints that survivied trimming either by inspecting the attribute metadata table in IL in case of IL trimming, or by inspecting the symbols or linker scripts in case of native compilation, or through a custom protocol if inspecting normal compilation outputs is undesirable.

API Proposal

namespace System.Runtime.InteropServices;

public sealed class UnmanagedCallersOnlyAttribute : Attribute
{
    public Type? AssociatedType;
}

API Usage

class MyObjectProxy
{
    [UnmanagedCallersOnly(EntryPoint = "MyObjectProxy_MyMethod", AssociatedType = typeof(MyObject))]
    static void MyMethod(IntPtr handle)
    {
        var obj = (MyObject)Runtime.GetNSObject(handle);
        obj.MyMethod();
    }
}

Alternative Designs

Risks

AssociatedType with the rule of "if the an object of this type could exist in the program" may not be sufficiently flexible and additional rules could be needed (for example, what if someone wants this to be generate if some other method was compiled/kept). There are no known uses cases beyond this one type-based rule right now though.

Metadata

Metadata

Assignees

No one assigned

    Labels

    api-ready-for-reviewAPI is ready for review, it is NOT ready for implementationarea-System.Runtime.InteropServicesblockingMarks issues that we want to fast track in order to unblock other important work

    Type

    No type
    No fields configured for issues without a type.

    Projects

    Status

    No status

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions