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

Nullable Reference Types & ICustomMarshaler #50144

Closed
jonpryor opened this issue Mar 24, 2021 · 4 comments
Closed

Nullable Reference Types & ICustomMarshaler #50144

jonpryor opened this issue Mar 24, 2021 · 4 comments
Labels
api-suggestion Early API idea and discussion, it is NOT ready for implementation area-System.Runtime.InteropServices

Comments

@jonpryor
Copy link
Member

Background and Motivation

The ICustomMarshaler interface should be re-reviewed in the context of Nullable Reference Types, in that null should be allowed everywhere. This is because ICustomMarshaler is an interface which dates to .NET 1.0, and existing interface implementations already assume that null can be used as parameters or return values.

Proposed API

--- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ICustomMarshaler.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ICustomMarshaler.cs
@@ -6,17 +6,13 @@ namespace System.Runtime.InteropServices
 namespace System.Runtime.InteropServices
 {     // This the base interface that must be implemented by all custom marshalers.
     public interface ICustomMarshaler
     {
-        object MarshalNativeToManaged(IntPtr pNativeData);
+        object? MarshalNativeToManaged(IntPtr pNativeData);
 
-        IntPtr MarshalManagedToNative(object ManagedObj);
+        IntPtr MarshalManagedToNative(object? ManagedObj);
 
         void CleanUpNativeData(IntPtr pNativeData);
 
-        void CleanUpManagedData(object ManagedObj);
+        void CleanUpManagedData(object? ManagedObj);
 
         int GetNativeDataSize();
     }

Usage Examples

Mono.Posix has had a type implementing ICustomMarshaler since 2005 which allows null values to be used:

https://github.com/mono/mono/blob/2020-02/mcs/class/Mono.Posix/Mono.Unix.Native/FileNameMarshaler.cs#L59-L75

	partial class FileNameMarshaler : ICustomMarshaler {
		public IntPtr MarshalManagedToNative (object obj)
		{
			string s = obj as string;
			if (s == null)
				return IntPtr.Zero;
			return UnixMarshal.StringToHeap (s, UnixEncoding.Instance);
		}

		public object MarshalNativeToManaged (IntPtr pNativeData)
		{
			return UnixMarshal.PtrToString (pNativeData, UnixEncoding.Instance);
		}
	}

Note: Above code is C# 1, and doesn't use nullable reference types, hence object and not object?.

Mono.Posix is not unique here; a cursory search through GitHub finds numerous examples which return or accept null:

Alternative Designs

If ICustomMarshaler isn't updated to allow the long-standing practice of accepting and returning null values, then existing code will need to be "uglified" to disable the warning around appropriate blocks of code, e.g.

https://github.com/mono/mono.posix/blob/25c94683fb9c5ec667d38382904a85d813aaaadf/src/Mono.Unix/Mono.Unix.Native/FileNameMarshaler.cs#L69-L86

Risks

This will cause new warnings to be emitted for any newly written code which implements the ICustomMarshaler interface, and such new code doesn't allow/permit `nulls.

This change will make it easier to "reasonably" migrate existing code which already supports null values.

@jonpryor jonpryor added the api-suggestion Early API idea and discussion, it is NOT ready for implementation label Mar 24, 2021
@dotnet-issue-labeler dotnet-issue-labeler bot added area-Interop-coreclr untriaged New issue has not been triaged by the area owner labels Mar 24, 2021
@stephentoub
Copy link
Member

cc: @jeffhandley

@jeffhandley
Copy link
Member

Thanks for filing this, @jonpryor, and for providing all of these details!

We've been treating changes to nullable annotations as bug fixes and documenting them in dotnet/docs#21202 by adding (unchecked) items into the issue description to capture the aggregated changes. If the changes are agreed upon, we can bypass the API review process and go straight to PR.

@ghost ghost added the in-pr There is an active PR which will close this issue when it is merged label Mar 25, 2021
jonpryor added a commit to jonpryor/runtime that referenced this issue Mar 25, 2021
Fixes: dotnet#50144

Re-review the [`ICustomMarshaler` interface][0] in the context of
Nullable Reference Types, so that `null` is allowed *everywhere*.
Do so because `ICustomMarshaler` is an interface which dates to
.NET 1.0, and existing interface implementations already assume that
`null` can be used as parameters or return values, including existing
unit tests within this repo!

```diff
--- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ICustomMarshaler.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ICustomMarshaler.cs
@@ -6,17 +6,13 @@ namespace System.Runtime.InteropServices
 namespace System.Runtime.InteropServices
 {     // This the base interface that must be implemented by all custom marshalers.
     public interface ICustomMarshaler
     {
-        object MarshalNativeToManaged(IntPtr pNativeData);
+        object? MarshalNativeToManaged(IntPtr pNativeData);

-        IntPtr MarshalManagedToNative(object ManagedObj);
+        IntPtr MarshalManagedToNative(object? ManagedObj);

         void CleanUpNativeData(IntPtr pNativeData);

-        void CleanUpManagedData(object ManagedObj);
+        void CleanUpManagedData(object? ManagedObj);

         int GetNativeDataSize();
     }
```

Additional rationale: a cursory [search through GitHub][1] finds
numerous examples which return or accept `null`, including:

  * [`TestLucene.CrapLord.Utf8ConstCustomMarshaler`][2]:
    allows `MarshalManagedToNative(null)`.

  * [`Script.DebugMarshaler`][3]:
    `MarshalNativeToManaged(IntPtr.Zero)` can return `null`, via
    `Marshal.PtrToStringAuto(IntPtr.Zero)`.

  * [`FFmpeg.AutoGen.UTF8Marshaler`][4]:
    returns `null` on `MarshalNativeToManaged(IntPtr.Zero)`, and
    allows `MarshalManagedToNative(null)`.

  * [`Microsoft.Win32.Security.SecurityAttributesMarshaler`][5]:
    returns `null` for `MarshalNativeToManaged(IntPtr.Zero)` and
    accepts `MarshalManagedToNative(null)`.

Updating `ICustomMarshaler` to use `object?` instead of `object` will
allow these (and other) existing types to be updated to use C#8
Nullable Reference Types without requiring semantic changes or
disabling the nullability checks around `ICustomMarshaler`.

[0]: https://docs.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.icustommarshaler?view=net-5.0
[1]: https://github.com/search?q=ICustomMarshaler+marshalnativetomanaged+language%3Acsharp&type=Code
[2]: https://github.com/ststeiger/WebRansack/blob/5da604ea4bbc9f35fa35c512b81f8d049fe47ed6/TestLucene/CrapLord/Marshallers/Utf8ConstCustomMarshaler.cs
[3]: https://github.com/defin/jrm-code-project/blob/0716e271f449b52bfabe4c2fefe8fc5e62902f42/LScript/DebugMarshaler.cs
[4]: https://github.com/jiangguang5201314/VideoRender/blob/b0fc7f172001673138b6bfd193f4ed71f46d4802/AVMedia/AVMedia/FFmpeg/ConstCharPtrMarshaler.cs
[5]: https://github.com/dineshkummarc/FlexWikiCore-2.1.0.274/blob/9b83495ac92a2d72a2be63bacfdff0662941fca5/FlexWikiCore-2.1.0.274-Source/lib/Win32Security/src/SecurityAttributesMarshaler.cs
@AaronRobinsonMSFT AaronRobinsonMSFT removed the untriaged New issue has not been triaged by the area owner label Mar 26, 2021
@ghost ghost removed the in-pr There is an active PR which will close this issue when it is merged label Mar 26, 2021
@jonpryor
Copy link
Member Author

The Mono & .NET runtime marshaler will never pass null or IntPtr.Zero to the ICustomMarshaler methods, and thus the interface doesn't need to be changed.

@stephentoub
Copy link
Member

Regardless, thanks for raising and discussing this, @jonpryor.

@ghost ghost locked as resolved and limited conversation to collaborators Apr 25, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
api-suggestion Early API idea and discussion, it is NOT ready for implementation area-System.Runtime.InteropServices
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants