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

Implement the non-blittable type marshalling proposal #302

Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
edcfded
First pass adding custom type marshaller.
jkoritzinsky Sep 25, 2020
40c8b87
Add support for a byref Value property and remove implementation rest…
jkoritzinsky Nov 3, 2020
fcdaa86
Add some negative tests for the custom native type marshalling.
jkoritzinsky Nov 4, 2020
086ef1c
Don't use stackalloc constructor if refkind is 'ref'.
jkoritzinsky Nov 4, 2020
f66d1d8
Add integration tests for non-blittable structs.
jkoritzinsky Nov 4, 2020
481bca3
Add more tests
jkoritzinsky Nov 4, 2020
60362ac
Merge branch 'feature/DllImportGenerator' of github.com:dotnet/runtim…
jkoritzinsky Nov 9, 2020
e26027d
Allow a GetPinnableReference call on the native type instead of allow…
jkoritzinsky Nov 12, 2020
7d729e1
Add missing cast.
jkoritzinsky Nov 13, 2020
e2e2f0e
Call GetPinnableReference.
jkoritzinsky Nov 13, 2020
b661b48
Assign stackalloc to pointer before creating span to work around life…
jkoritzinsky Nov 13, 2020
433275e
Merge branch 'feature/DllImportGenerator' of github.com:dotnet/runtim…
jkoritzinsky Nov 14, 2020
32c6336
Add custom message for errors.
jkoritzinsky Nov 14, 2020
7116872
Merge branch 'feature/DllImportGenerator' of github.com:dotnet/runtim…
jkoritzinsky Nov 16, 2020
f0b8241
Fix simple flags logic error.
jkoritzinsky Nov 16, 2020
4fae329
Move tests to CompileFails.cs
jkoritzinsky Nov 17, 2020
b359225
Add analyzer error for ref Value property.
jkoritzinsky Nov 17, 2020
563d39f
Update comments.
jkoritzinsky Nov 17, 2020
dfd4e26
Add simple MarshalUsing test
jkoritzinsky Nov 17, 2020
8bc0d70
Add more tests. Fix a bug with using a marshaller that supports pinni…
jkoritzinsky Nov 17, 2020
af8b53f
Fix typo in DNNE type hint attribute.
jkoritzinsky Nov 17, 2020
cba7503
Unify on boolean operators at the start of a line instead of at the e…
jkoritzinsky Nov 18, 2020
5be8022
Minor cleanup
jkoritzinsky Nov 19, 2020
28c1c58
Rename tests.
jkoritzinsky Nov 19, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
using System.Runtime.InteropServices;

using SharedTypes;

using Xunit;

namespace DllImportGenerator.IntegrationTests
{
partial class NativeExportsNE
{
[GeneratedDllImport(nameof(NativeExportsNE), EntryPoint = "stringcontainer_deepduplicate")]
public static partial void DeepDuplicateStrings(StringContainer strings, out StringContainer pStringsOut);

[GeneratedDllImport(nameof(NativeExportsNE), EntryPoint = "get_long_bytes_as_double")]
public static partial double GetLongBytesAsDouble([MarshalUsing(typeof(DoubleToLongMarshaler))] double d);

[GeneratedDllImport(nameof(NativeExportsNE), EntryPoint = "negate_bools")]
public static partial void NegateBools(
BoolStruct boolStruct,
out BoolStruct pBoolStructOut);

[GeneratedDllImport(nameof(NativeExportsNE), EntryPoint = "double_int_ref")]
public static partial IntWrapper DoubleIntRef(IntWrapper pInt);
}

public class NonBlittableStructTests
{
[Fact]
public void NonBlittableStructWithFree()
{
var stringContainer = new StringContainer
{
str1 = "Foo",
str2 = "Bar"
};

NativeExportsNE.DeepDuplicateStrings(stringContainer, out var stringContainer2);

Assert.Equal(stringContainer, stringContainer2);
}

[Fact]
public void MarshalUsing()
{
double d = 1234.56789;

Assert.Equal(d, NativeExportsNE.GetLongBytesAsDouble(d));
}

[Fact]
public void NonBlittableStructWithoutAllocation()
{
var boolStruct = new BoolStruct
{
b1 = true,
b2 = false,
b3 = true
};

NativeExportsNE.NegateBools(boolStruct, out BoolStruct boolStructNegated);

Assert.Equal(!boolStruct.b1, boolStructNegated.b1);
Assert.Equal(!boolStruct.b2, boolStructNegated.b2);
Assert.Equal(!boolStruct.b3, boolStructNegated.b3);
}

[Fact]
public void GetPinnableReferenceMarshalling()
{
int originalValue = 42;
var wrapper = new IntWrapper { i = originalValue };

var retVal = NativeExportsNE.DoubleIntRef(wrapper);

Assert.Equal(originalValue * 2, wrapper.i);
Assert.Equal(originalValue * 2, retVal.i);
}
}
}
294 changes: 294 additions & 0 deletions DllImportGenerator/DllImportGenerator.UnitTests/CodeSnippets.cs
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,20 @@ partial class Test
out {typeName} pOut);
}}";

/// <summary>
/// Declaration with parameters.
/// </summary>
public static string BasicParametersAndModifiersNoRef(string typeName) => @$"
using System.Runtime.InteropServices;
partial class Test
{{
[GeneratedDllImport(""DoesNotExist"")]
public static partial {typeName} Method(
{typeName} p,
in {typeName} pIn,
out {typeName} pOut);
}}";

public static string BasicParametersAndModifiers<T>() => BasicParametersAndModifiers(typeof(T).ToString());

/// <summary>
Expand Down Expand Up @@ -446,5 +460,285 @@ public static partial void Method(
}}";

public static string ArrayParameterWithNestedMarshalInfo<T>(UnmanagedType nestedMarshalType) => ArrayParameterWithNestedMarshalInfo(typeof(T).ToString(), nestedMarshalType);
public static string CustomStructMarshallingParametersAndModifiers = BasicParametersAndModifiers("S") + @"
[NativeMarshalling(typeof(Native))]
struct S
{
public bool b;
}

struct Native
Copy link
Member

Choose a reason for hiding this comment

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

It would be nice to try to establish naming convention for these and use it here. Native is very generic and it does not scale to have more than one in the given namespace.

Copy link
Member Author

Choose a reason for hiding this comment

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

I agree that we need to establish a naming convention for these types, but I don't think we need to do that immediately. We'll need to do it when we decide we want to support generating the native types for structs.

{
private int i;
public Native(S s)
{
i = s.b ? 1 : 0;
}

public S ToManaged() => new S { b = i != 0 };
}
";

public static string CustomStructMarshallingStackallocParametersAndModifiersNoRef = BasicParametersAndModifiersNoRef("S") + @"
[NativeMarshalling(typeof(Native))]
struct S
{
public bool b;
}

struct Native
{
private int i;
public Native(S s, System.Span<byte> b)
{
i = s.b ? 1 : 0;
}

public S ToManaged() => new S { b = i != 0 };

public const int StackBufferSize = 1;
}
";
public static string CustomStructMarshallingStackallocOnlyRefParameter = BasicParameterWithByRefModifier("ref", "S") + @"
[NativeMarshalling(typeof(Native))]
struct S
{
public bool b;
}

struct Native
{
private int i;
public Native(S s, System.Span<byte> b)
{
i = s.b ? 1 : 0;
}

public S ToManaged() => new S { b = i != 0 };

public const int StackBufferSize = 1;
}
";
public static string CustomStructMarshallingOptionalStackallocParametersAndModifiers = BasicParametersAndModifiers("S") + @"
[NativeMarshalling(typeof(Native))]
struct S
{
public bool b;
}

struct Native
{
private int i;
public Native(S s, System.Span<byte> b)
{
i = s.b ? 1 : 0;
}
public Native(S s)
{
i = s.b ? 1 : 0;
}

public S ToManaged() => new S { b = i != 0 };

public const int StackBufferSize = 1;
}
";

public static string CustomStructMarshallingStackallocValuePropertyParametersAndModifiersNoRef = BasicParametersAndModifiersNoRef("S") + @"
[NativeMarshalling(typeof(Native))]
struct S
{
public bool b;
}

struct Native
{
public Native(S s, System.Span<byte> b)
{
Value = s.b ? 1 : 0;
}

public S ToManaged() => new S { b = Value != 0 };

public int Value { get; set; }
Copy link
Member

Choose a reason for hiding this comment

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

Native type with Value property

To reduce number of locals, I may be nice to pass the Value in directly for the simple case, e.g.:

__retVal_gen_native = Method__PInvoke__(__p_gen_native__marshaler.Value, ...);


public const int StackBufferSize = 1;
}
";
public static string CustomStructMarshallingValuePropertyParametersAndModifiers = BasicParametersAndModifiers("S") + @"
[NativeMarshalling(typeof(Native))]
struct S
{
public bool b;
}

struct Native
{
public Native(S s)
{
Value = s.b ? 1 : 0;
}

public S ToManaged() => new S { b = Value != 0 };

public int Value { get; set; }
}
";
public static string CustomStructMarshallingPinnableParametersAndModifiers = BasicParametersAndModifiers("S") + @"
[NativeMarshalling(typeof(Native))]
class S
{
public int i;

public ref int GetPinnableReference() => ref i;
}

unsafe struct Native
{
private int* ptr;
public Native(S s)
{
ptr = (int*)Marshal.AllocHGlobal(sizeof(int));
*ptr = s.i;
}

public S ToManaged() => new S { i = *ptr };

public nint Value
{
get => (nint)ptr;
set => ptr = (int*)value;
}
}
";

public static string CustomStructMarshallingByRefValuePropertyIn = @"
using System.Runtime.InteropServices;

[NativeMarshalling(typeof(Native))]
class S
{
public byte c;
}

unsafe struct Native
{
private S value;

public Native(S s) : this()
{
value = s;
}

public ref byte Value { get => ref value.c; }
}

partial class Test
{
[GeneratedDllImport(""DoesNotExist"")]
public static partial void Method(
S s,
in S sIn);
}
";

public static string CustomStructMarshallingByRefValuePropertyRefOutReturn = @"
using System.Runtime.InteropServices;

[NativeMarshalling(typeof(Native))]
class S
{
public byte c;
}

unsafe struct Native
{
private S value;

public Native(S s) : this()
{
value = s;
}

public ref byte Value { get => ref value.c; }
}

partial class Test
{
[GeneratedDllImport(""DoesNotExist"")]
public static partial S Method(
ref S sRef,
out S sOut);
}
";

public static string BasicParameterWithByRefModifier(string byRefKind, string typeName) => @$"
using System.Runtime.InteropServices;
partial class Test
{{
[GeneratedDllImport(""DoesNotExist"")]
public static partial void Method(
{byRefKind} {typeName} p);
}}";

public static string BasicReturnType(string typeName) => @$"
using System.Runtime.InteropServices;
partial class Test
{{
[GeneratedDllImport(""DoesNotExist"")]
public static partial {typeName} Method();
}}";

public static string CustomStructMarshallingManagedToNativeOnlyOutParameter => BasicParameterWithByRefModifier("out", "S") + @"
[NativeMarshalling(typeof(Native))]
[StructLayout(LayoutKind.Sequential)]
struct S
{
public bool b;
}

struct Native
{
private int i;
public Native(S s)
{
i = s.b ? 1 : 0;
}
}
";

public static string CustomStructMarshallingManagedToNativeOnlyReturnValue => BasicReturnType("S") + @"
[NativeMarshalling(typeof(Native))]
[StructLayout(LayoutKind.Sequential)]
struct S
{
public bool b;
}

struct Native
{
private int i;
public Native(S s)
{
i = s.b ? 1 : 0;
}
}
";

public static string CustomStructMarshallingNativeToManagedOnlyInParameter => BasicParameterWithByRefModifier("in", "S") + @"
[NativeMarshalling(typeof(Native))]
struct S
{
public bool b;
}

[StructLayout(LayoutKind.Sequential)]
struct Native
{
private int i;
public S ToManaged() => new S { b = i != 0 };
}
";

}
}
Loading