[API Proposal]: Specify supported custom type marshalling features on CustomTypeMarshallerAttribute

This issue has been created since 2022-03-02.

Background and motivation

For our source-generated interop work, we have recently approved the System.Runtime.InteropServices.CustomTypeMarshallerAttribute, which we will use to mark user-defined custom marshallers to help us guide users to define their marshaller types with the correct shapes (as interfaces are not usable with ref structs).

Currently, the design calls for us to emit code fixes to offer up the different members that users may want to add. However, we have no mechanism to allow users to say "I want to support these features in my marshaller, so validate at marshaller authoring time that I have the right members".

This issue proposes an enum and an extension of the CustomTypeMarshallerAttribute to enable developers to declaratively state which features they plan to support. This will allow our analyzer to enforce that the members required by each shape for each feature are present, and it will allow users of the marshaller types to know which features they can use when using the marshaller.

Our analyzer will enforce that all members required for the features specified by the users are present. The interop source generator will only use the features of the marshaller type specified in the attribute, even if other members are specified.

API Proposal

namespace System.Runtime.InteropServices
{
    public class CustomTypeMarshallerAttribute : Attribute
    {
+        public CustomTypeMarshallerDirection Direction { get; set; } = CustomTypeMarshallerDirection.Ref;
+        public CustomTypeMarshallerFeatures Features { get; set; }
    }

+    [Flags]
+    public enum CustomTypeMarshallerDirection
+    {
+        In = 0x1,
+        Out = 0x2,
+        Ref = CustomTypeMarshallerDirection.In | CustomTypeMarshallerDirection.Out
+    }
+    [Flags]
+    public enum CustomTypeMarshallerFeatures
+    {
+        None = 0x0,
+        UnmanagedResources = 0x1,
+        CallerAllocatedBuffer = 0x2,
+        TwoStageMarshalling = 0x4,
+    }
}

API Usage

[CustomTypeMarshaller(typeof(string), Features = CustomTypeMarshallerFeatures.UnmanagedResources | CustomTypeMarshallerFeatures.CallerAllocatedBuffer | CustomTypeMarshallerFeatures.TwoStageMarshalling, BufferSize = 0x200 )]
public struct Utf16StringMarshaller
{

    // Required for In direction
    public Utf16StringMarshaller(string s);
    // Required for In direction and CallerAllocatedBuffer feature
    public Utf16StringMarshaller(string s, Span<byte> buffer);

   // Required for In direction and TwoStageMarsahalling feature
    public IntPtr ToNativeValue();
   // Required for Out direction and TwoStageMarsahalling feature
    public void FromNativeValue(IntPtr value);

   // Required for Out direction
    public string ToManaged();

    // Required for UnmanagedResources feature
    public void FreeNative();
}

public struct MultiBoolStruct
{
    public bool b1;
    public bool b2;
}

[CustomTypeMarshaller(typeof(MultiBoolStruct), Direction = CustomTypeMarshallerDirection.In )]
public struct MultiBoolStructMarshaller
{
    // Required for In direction
    public MultiBoolStructMarshaller(MultiBoolStruct s);
}

Alternative Designs

We could instead keep the API as-is today and require the generators to inspect the custom marshaller type's API surface as usage time to ensure that it has all of the required members.

Risks

Enforcement will depend on an analyzer, with can be turned off. However, if someone turns off the analyzer then they'll just end up with a type that can't be used by the source generator due to their own choices, so I don't think this is a large concern.

We have a limited number of bits for features, so we need to make sure that the features are general enough that we won't exceed our limited bitmask size.

jkoritzinsky wrote this answer on 2022-03-03
terrajobst wrote this answer on 2022-03-08

Video

  • We should add a zero-value for CustomTypeMarshallerDirection such that default initialized values have an associated member. We should call it None and simply reject this as invalid for CustomTypeMarshallerAttribute.Direction. We should hide it.
namespace System.Runtime.InteropServices
{
    public partial class CustomTypeMarshallerAttribute : Attribute
    {
        public CustomTypeMarshallerDirection Direction { get; set; } = CustomTypeMarshallerDirection.Ref;
        public CustomTypeMarshallerFeatures Features { get; set; }
    }
    [Flags]
    public enum CustomTypeMarshallerDirection
    {
        [EditorBrowsable(EditorBrowsableState.Never)]
        None = 0x0,
        In = 0x1,
        Out = 0x2,
        Ref = CustomTypeMarshallerDirection.In | CustomTypeMarshallerDirection.Out
    }
    [Flags]
    public enum CustomTypeMarshallerFeatures
    {
        None = 0x0,
        UnmanagedResources = 0x1,
        CallerAllocatedBuffer = 0x2,
        TwoStageMarshalling = 0x4,
    }
}
marhja wrote this answer on 2022-03-15

Seems like the Framework design guidelines only recommend a None value for simple enums. It recommends that for flags enums, like CustomTypeMarshallerDirection, enum values of zero should be avoided unless it means all flags are cleared, which makes no sense in this case.

More Details About Repo
Owner Name dotnet
Repo Name runtime
Full Name dotnet/runtime
Language C#
Created Date 2019-09-24
Updated Date 2022-08-12
Star Count 9759
Watcher Count 392
Fork Count 3329
Issue Count 7695

YOU MAY BE INTERESTED

Issue Title Created Date Comment Count Updated Date
CDN URL returns 403 forbidden 1 2021-01-08 2022-08-04
install pycharm on demand and open on developer menu 1 2022-03-04 2022-04-14
add support plugin to install & open app on demand; webtop version only 2 2022-03-05 2022-08-11
remove message: nohup: appending output to 'nohup.out' 1 2022-03-04 2022-04-14
Multiple rows in custom table 5 2022-01-06 2022-08-02
Allow for user-configured naming conventions of restart files 8 2022-04-20 2022-07-25
Expand use of variable output 0 2022-04-20 2022-07-25
Check or implement ?(x, y) completion 0 2022-03-14 2022-07-13
Tdarr_Plugin_A47j_FFMPEG_NVENC_HEVC_Video_Only - Not passing through bit rates/reporting as NaN? 0 2022-06-03 2022-08-13
Add a linter for Semantic Line Breaks in Markdown files 0 2022-03-24 2022-08-05
dbConnect(check_interrupt = TRUE) broken? 0 2021-09-19 2022-08-08
Assert failed: Tried to remove node # that still contains references pointing to it. 1 2022-04-27 2022-08-07
When is the i_config attribute set? 2 2022-01-14 2022-07-26
Wrong forecast results 5 2022-02-07 2022-08-05
show link in a cell when mouse hover over the row 2 2021-12-21 2022-08-05
mod_wsgi install on OS X 12.1 on macmini MI chip 5 2022-01-06 2022-08-02
Control theme_options first sub-page/nav-item title 2 2021-08-26 2022-08-05
wordpress 5.8 - rich missing tag <p> 2 2021-08-16 2022-08-05
GetInputRate and GetOutputRate need to be removed from CSharp interface 1 2021-12-31 2022-08-10
na_rm argument isn't tested 1 2022-03-25 2022-07-26
Make solver information, stats available programmatically rather than just printing 6 2020-01-05 2022-08-02
Add `JSON.printfile` function 0 2021-07-27 2022-08-05
Branch reference is erroneously moved on `git move` 5 2021-12-29 2022-08-10
Re-enable localization tests 0 2021-08-24 2022-08-13
Document update using dune as primary build system? 3 2022-06-27 2022-08-09
Only new, but I think it'd be nice to add :) 10 2022-01-31 2022-08-05
Using $transaction with an update query on a table with a compositie key fails with wrong message 0 2022-02-07 2022-07-29
Disable IDEA Inspection for `AnnotationClass` 6 2022-06-08 2022-08-06
autoscale.log rotate not working 1 2021-06-17 2022-08-11
Change K8s API from apiextensions.k8s.io/v1beta1 to apiextensions.k8s.io/v1 4 2021-12-07 2022-07-29
Tokens are not been created 3 2022-01-20 2022-08-07
Using `store_offset` with `OwnedMessage` 3 2021-05-31 2022-07-03
Migrate node_add_ssh_key 0 2022-07-22 2022-08-05
protobuf v1.4.2 Package "github.com/golang/protobuf/protoc-gen-go/generator" is deprecated 1 2020-07-02 2022-07-21
Add a parameter to taint node 1 2020-07-17 2022-07-03
Add 'wargames' 0 2019-07-10 2021-11-17
Bug: possible bug in zpipe.c example 2 2020-12-08 2022-08-12
Difficulty calculator outputs negative AR/OD for some beatmaps 3 2022-06-22 2022-07-23
Switch GPU without reboot 18 2018-12-20 2022-08-13
OBS UI issues on dual monitor setup with different resolutions 0 2022-03-09 2022-08-05
GridView component typing definition of gridSize prop 1 2022-03-01 2022-08-05
Chart Feature : option to use DateTime as axis without hours 3 2022-06-21 2022-08-03
tls_double_channel test gets stuck 3 2021-10-04 2022-08-05
Allow not copy libs (for particular launch4j task) 0 2020-09-18 2021-10-22
Context manager operation thread-safe? 1 2021-12-30 2022-07-31
open apigw服务可以开源出来吗? 1 2021-10-13 2022-08-03
Jenkins not restarted after plugin installation if include_role is used 6 2017-08-09 2022-07-29
[SUPPORT] Write audit publish 8 2022-08-01 2022-08-10
[SUPPORT]Caused by: java.util.NoSuchElementException: No value present in Option 1 2022-08-01 2022-08-10
Have conflict with setDefaultUncaughtExceptionHandler 2 2019-04-15 2022-07-27