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

Add tests for generic types/values with the Marshal APIs #55533

Merged
merged 4 commits into from
Jul 14, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Expand Up @@ -6,6 +6,12 @@ namespace System.Runtime.InteropServices.Tests.Common
[ComVisible(true)]
public class GenericClass<T> { }

[StructLayout(LayoutKind.Sequential)]
public class SequentialGenericClass<T>
{
public T field;
}

[ComVisible(true)]
public class NonGenericClass { }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,34 @@ public void OffsetOf_NoLayoutPoint_ThrowsArgumentException()
AssertExtensions.Throws<ArgumentException>(null, () => Marshal.OffsetOf<NoLayoutPoint>(nameof(NoLayoutPoint.x)));
}

[Fact]
public void OffsetOf_Field_ValueType_Generic()
{
Assert.Equal((IntPtr)4, Marshal.OffsetOf<Point2<int>>(nameof(Point2<int>.y)));
Assert.Equal((IntPtr)8, Marshal.OffsetOf<Point2<ulong>>(nameof(Point2<int>.y)));
Assert.Equal((IntPtr)4, Marshal.OffsetOf<Point2<float>>(nameof(Point2<int>.y)));
Assert.Equal((IntPtr)8, Marshal.OffsetOf<Point2<double>>(nameof(Point2<int>.y)));

// [COMPAT] Non-blittable generic types with value-type generic arguments are supported in OffsetOf since they've always been allowed
// and it likely doesn't meet the bar to break back-compat.
Assert.Equal((IntPtr)1, Marshal.OffsetOf<Point2<char>>(nameof(Point2<int>.y)));
Assert.Equal((IntPtr)1, Marshal.OffsetOf<Point2<byte>>(nameof(Point2<int>.y)));
elinor-fung marked this conversation as resolved.
Show resolved Hide resolved
}

[Fact]
public void OffsetOf_Field_ReferenceType_Generic()
{
// [COMPAT] Generic types with value-type generic arguments are supported in OffsetOf since they've always been allowed
// and it likely doesn't meet the bar to break back-compat.
Assert.Equal((IntPtr)4, Marshal.OffsetOf<Point2Class<int>>(nameof(Point2Class<int>.y)));
Assert.Equal((IntPtr)8, Marshal.OffsetOf<Point2Class<ulong>>(nameof(Point2Class<int>.y)));
Assert.Equal((IntPtr)4, Marshal.OffsetOf<Point2Class<float>>(nameof(Point2Class<int>.y)));
Assert.Equal((IntPtr)8, Marshal.OffsetOf<Point2Class<double>>(nameof(Point2Class<int>.y)));

Assert.Equal((IntPtr)1, Marshal.OffsetOf<Point2Class<char>>(nameof(Point2Class<int>.y)));
Assert.Equal((IntPtr)1, Marshal.OffsetOf<Point2Class<byte>>(nameof(Point2Class<int>.y)));
}

public class NonRuntimeType : Type
{
public override FieldInfo GetField(string name, BindingFlags bindingAttr)
Expand Down Expand Up @@ -495,6 +523,19 @@ struct FieldAlignmentTest_Variant

public short s; // 2 bytes
// 6 bytes of padding
};
}

struct Point2<T>
{
public T x;
public T y;
}

[StructLayout(LayoutKind.Sequential)]
class Point2Class<T>
{
public T x;
public T y;
}
#pragma warning restore 169, 649, 618
}
Original file line number Diff line number Diff line change
Expand Up @@ -138,18 +138,31 @@ public void PtrToStructure_NullStructure_ThrowsArgumentNullException()
AssertExtensions.Throws<ArgumentNullException>("structure", () => Marshal.PtrToStructure<object>((IntPtr)1, null));
}

public static IEnumerable<object[]> PtrToStructure_GenericClass_TestData()
[Fact]
public void PtrToStructure_AutoLayoutClass_ThrowsArgumentException()
{
yield return new object[] { new GenericClass<string>() };
yield return new object[] { new GenericStruct<string>() };
AssertExtensions.Throws<ArgumentException>("structure", () => Marshal.PtrToStructure((IntPtr)1, (object)new NonGenericClass()));
AssertExtensions.Throws<ArgumentException>("structure", () => Marshal.PtrToStructure((IntPtr)1, new NonGenericClass()));
}

[Theory]
[MemberData(nameof(PtrToStructure_GenericClass_TestData))]
public void PtrToStructure_GenericObject_ThrowsArgumentException(object o)
[Fact]
public unsafe void PtrToStructure_GenericLayoutClass_Generic()
{
int i = 42;
IntPtr ptr = (IntPtr)(&i);
SequentialGenericClass<int> obj = new SequentialGenericClass<int>();
Marshal.PtrToStructure(ptr, obj);
Assert.Equal(i, obj.field);
}

[Fact]
public unsafe void PtrToStructure_GenericLayoutClass()
{
AssertExtensions.Throws<ArgumentException>("structure", () => Marshal.PtrToStructure((IntPtr)1, o));
AssertExtensions.Throws<ArgumentException>("structure", () => Marshal.PtrToStructure<object>((IntPtr)1, o));
int i = 42;
IntPtr ptr = (IntPtr)(&i);
SequentialGenericClass<int> obj = new SequentialGenericClass<int>();
Marshal.PtrToStructure(ptr, (object)obj);
Assert.Equal(i, obj.field);
}

public static IEnumerable<object[]> PtrToStructure_ObjectNotValueClass_TestData()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,34 @@ public void SizeOf_InvalidType_ThrowsArgumentException(Type type, string paramNa
AssertExtensions.Throws<ArgumentException>(paramName, () => Marshal.SizeOf(type));
}

[Fact]
public void SizeOf_GenericStruct_Value_NonGeneric()
{
GenericStruct<int> value = default;
Assert.Equal(8, Marshal.SizeOf((object)value));
}

[Fact]
public void SizeOf_GenericStruct_Value_Generic()
{
GenericStruct<int> value = default;
Assert.Equal(8, Marshal.SizeOf(value));
}

[Fact]
public void SizeOf_GenericClass_Value_NonGeneric()
{
SequentialGenericClass<int> value = new();
Assert.Equal(4, Marshal.SizeOf((object)value));
}

[Fact]
public void SizeOf_GenericClass_Value_Generic()
{
SequentialGenericClass<int> value = new();
Assert.Equal(4, Marshal.SizeOf(value));
}

public struct TestStructWithEnumArray
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
Expand Down