Skip to content

Commit

Permalink
Ensure that the record packing and calling convention are correct reg…
Browse files Browse the repository at this point in the history
…ardless of -m32 or -m64
  • Loading branch information
tannergooding committed Aug 4, 2021
1 parent 69bf841 commit 4f7af5d
Show file tree
Hide file tree
Showing 39 changed files with 609 additions and 441 deletions.
10 changes: 6 additions & 4 deletions sources/ClangSharp.PInvokeGenerator/Abstractions/StructDesc.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,19 +63,21 @@ public StructLayoutAttribute LayoutAttribute
{
Debug.Assert(layout.Kind == LayoutKind.Explicit);

StructLayoutAttribute attribute = new(layout.Kind);
var attribute = new StructLayoutAttribute(layout.Kind);

if (layout.Pack < layout.MaxFieldAlignment)
if (layout.Pack != 0)
{
attribute.Pack = (int)layout.Pack;
}

return attribute;
}

if (layout.Pack < layout.MaxFieldAlignment)
if (layout.Pack != 0)
{
return new StructLayoutAttribute(layout.Kind) {Pack = (int)layout.Pack};
return new StructLayoutAttribute(layout.Kind) {
Pack = (int)layout.Pack
};
}

return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -433,15 +433,16 @@ public void EndFunctionOrDelegate(bool isVirtual, bool isBodyless)

public void BeginStruct<TCustomAttrGeneratorData>(in StructDesc<TCustomAttrGeneratorData> info)
{
if (info.LayoutAttribute is { } attribute)
if (info.LayoutAttribute is not null)
{
AddUsingDirective("System.Runtime.InteropServices");
WriteIndented("[StructLayout(LayoutKind.");
Write(attribute.Value);
if (attribute.Pack != default)
Write(info.LayoutAttribute.Value);

if (info.LayoutAttribute.Pack != 0)
{
Write(", Pack = ");
Write(attribute.Pack);
Write(info.LayoutAttribute.Pack);
}

WriteLine(")]");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -455,11 +455,11 @@ private void VisitFunctionDecl(FunctionDecl functionDecl)
var callingConventionName = GetCallingConvention(functionDecl, cxxRecordDecl, type);

var isDllImport = body is null && !isVirtual;
var entryPoint = isDllImport ? functionDecl.Handle.Mangling.CString : null;
var entryPoint = "";

if (entryPoint == $"_{functionDecl.Name}")
if (isDllImport)
{
entryPoint = functionDecl.Name;
entryPoint = functionDecl.IsExternC ? GetCursorName(functionDecl) : functionDecl.Handle.Mangling.CString;
}

var needsReturnFixup = isVirtual && NeedsReturnFixup(cxxMethodDecl);
Expand Down Expand Up @@ -1151,7 +1151,7 @@ private void VisitRecordDecl(RecordDecl recordDecl)
Alignment64 = alignment64,
Size32 = size32,
Size64 = size64,
Pack = alignment,
Pack = recordDecl.HasAttrs && recordDecl.Attrs.Any((attr) => attr.Kind == CX_AttrKind.CX_AttrKind_MaxFieldAlignment) && ((alignment != alignment32) || (alignment != alignment64)) ? alignment : 0,
MaxFieldAlignment = maxAlignm,
Kind = layoutKind
},
Expand Down Expand Up @@ -2201,7 +2201,7 @@ void VisitConstantArrayFieldDecl(RecordDecl recordDecl, FieldDecl constantArray)
Alignment64 = alignment64,
Size32 = size32,
Size64 = size64,
Pack = alignment,
Pack = recordDecl.HasAttrs && recordDecl.Attrs.Any((attr) => attr.Kind == CX_AttrKind.CX_AttrKind_MaxFieldAlignment) && ((alignment != alignment32) || (alignment != alignment64)) ? alignment : 0,
MaxFieldAlignment = maxAlignm,
Kind = LayoutKind.Sequential
},
Expand Down
8 changes: 8 additions & 0 deletions sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -877,6 +877,10 @@ private CallingConvention GetCallingConvention(Cursor cursor, Cursor context, Ty
{
case CXCallingConv.CXCallingConv_C:
{
if ((cursor is CXXMethodDecl cxxMethodDecl) && cxxMethodDecl.IsInstance)
{
return CallingConvention.ThisCall;
}
return CallingConvention.Cdecl;
}

Expand Down Expand Up @@ -3887,6 +3891,10 @@ private void Visit(Cursor cursor)
{
VisitDecl(decl);
}
else if (cursor is PreprocessedEntity preprocessedEntity)
{
VisitPreprocessedEntity(preprocessedEntity);
}
else if (cursor is Ref @ref)
{
VisitRef(@ref);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -248,15 +248,16 @@ public void BeginStruct<TCustomAttrGeneratorData>(in StructDesc<TCustomAttrGener
_ = _sb.Append(" unsafe=\"true\"");
}

if (info.LayoutAttribute is { } attribute)
if (info.LayoutAttribute is not null)
{
_ = _sb.Append(" layout=\"");
_ = _sb.Append(attribute.Value);
_ = _sb.Append(info.LayoutAttribute.Value);
_ = _sb.Append('"');
if (attribute.Pack != default)

if (info.LayoutAttribute.Pack != 0)
{
_ = _sb.Append(" pack=\"");
_ = _sb.Append(attribute.Pack);
_ = _sb.Append(info.LayoutAttribute.Pack);
_ = _sb.Append('"');
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
},
"GenerateLocal": {
"commandName": "Project",
"commandLineArgs": "-c help"
"commandLineArgs": "@generate.rsp",
"workingDirectory": "C:\\Users\\tagoo\\Source\\repos\\terrafx.interop.windows\\generation\\um\\minidumpapiset"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,9 @@ public abstract class StructDeclarationTest : PInvokeGeneratorTest
[Fact]
public abstract Task NoDefinitionTest();

[Fact]
public abstract Task PackTest();

[Fact]
public abstract Task PointerToSelfTest();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,14 +164,12 @@ int MyInt32Method()
}
};
";
var callConv = "Cdecl";
var entryPoint = RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? "__ZN8MyStruct12MyVoidMethodEv" : "_ZN8MyStruct12MyVoidMethodEv";

if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
if (!Environment.Is64BitProcess)
{
callConv = "ThisCall";
entryPoint = "?MyVoidMethod@MyStruct@@QAEXXZ";
}
else
Expand All @@ -186,7 +184,7 @@ namespace ClangSharp.Test
{{
public partial struct MyStruct
{{
[DllImport(""ClangSharpPInvokeGenerator"", CallingConvention = CallingConvention.{callConv}, EntryPoint = ""{entryPoint}"", ExactSpelling = true)]
[DllImport(""ClangSharpPInvokeGenerator"", CallingConvention = CallingConvention.ThisCall, EntryPoint = ""{entryPoint}"", ExactSpelling = true)]
public static extern void MyVoidMethod();
public int MyInt32Method()
Expand Down Expand Up @@ -417,13 +415,6 @@ public override Task NewKeywordVirtualTest()
virtual int GetType(int objA, int objB) = 0;
};";

var callConv = "Cdecl";

if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && !Environment.Is64BitProcess)
{
callConv = "ThisCall";
}

var expectedOutputContents = $@"using System;
using System.Runtime.InteropServices;
Expand All @@ -433,13 +424,13 @@ public unsafe partial struct MyStruct
{{
public void** lpVtbl;
[UnmanagedFunctionPointer(CallingConvention.{callConv})]
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
public delegate int _GetType(MyStruct* pThis, int obj);
[UnmanagedFunctionPointer(CallingConvention.{callConv})]
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
public delegate int _GetType1(MyStruct* pThis);
[UnmanagedFunctionPointer(CallingConvention.{callConv})]
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
public delegate int _GetType2(MyStruct* pThis, int objA, int objB);
public int GetType(int obj)
Expand Down Expand Up @@ -481,12 +472,10 @@ public override Task NewKeywordVirtualWithExplicitVtblTest()
virtual int GetType(int objA, int objB) = 0;
};";

var callConv = "Cdecl";
var nativeCallConv = "";

if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && !Environment.Is64BitProcess)
{
callConv = "ThisCall";
nativeCallConv = " __attribute__((thiscall))";
}

Expand All @@ -499,13 +488,13 @@ public unsafe partial struct MyStruct
{{
public Vtbl* lpVtbl;
[UnmanagedFunctionPointer(CallingConvention.{callConv})]
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
public delegate int _GetType(MyStruct* pThis, int obj);
[UnmanagedFunctionPointer(CallingConvention.{callConv})]
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
public delegate int _GetType1(MyStruct* pThis);
[UnmanagedFunctionPointer(CallingConvention.{callConv})]
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
public delegate int _GetType2(MyStruct* pThis, int objA, int objB);
public int GetType(int obj)
Expand Down Expand Up @@ -806,13 +795,6 @@ virtual char MyInt8Method()
};
";

var callConv = "Cdecl";

if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && !Environment.Is64BitProcess)
{
callConv = "ThisCall";
}

var expectedOutputContents = $@"using System;
using System.Runtime.InteropServices;
Expand All @@ -822,17 +804,17 @@ public unsafe partial struct MyStruct
{{
public void** lpVtbl;
[UnmanagedFunctionPointer(CallingConvention.{callConv})]
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
public delegate void _MyVoidMethod(MyStruct* pThis);
[UnmanagedFunctionPointer(CallingConvention.{callConv})]
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
[return: NativeTypeName(""char"")]
public delegate sbyte _MyInt8Method(MyStruct* pThis);
[UnmanagedFunctionPointer(CallingConvention.{callConv})]
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
public delegate int _MyInt32Method(MyStruct* pThis);
[UnmanagedFunctionPointer(CallingConvention.{callConv})]
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
public delegate void* _MyVoidStarMethod(MyStruct* pThis);
public void MyVoidMethod()
Expand Down Expand Up @@ -891,13 +873,6 @@ virtual char MyInt8Method()
};
";

var callConv = "Cdecl";

if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && !Environment.Is64BitProcess)
{
callConv = "ThisCall";
}

var expectedOutputContents = $@"using System;
using System.Runtime.InteropServices;
Expand All @@ -907,17 +882,17 @@ public unsafe partial struct MyStruct
{{
public void** lpVtbl;
[UnmanagedFunctionPointer(CallingConvention.{callConv})]
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
public delegate void _MyVoidMethod(MyStruct* pThis);
[UnmanagedFunctionPointer(CallingConvention.{callConv})]
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
[return: NativeTypeName(""char"")]
public delegate sbyte _MyInt8Method(MyStruct* pThis);
[UnmanagedFunctionPointer(CallingConvention.{callConv})]
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
public delegate int _MyInt32Method(MyStruct* pThis);
[UnmanagedFunctionPointer(CallingConvention.{callConv})]
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
public delegate void* _MyVoidStarMethod(MyStruct* pThis);
[VtblIndex(0)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -495,13 +495,6 @@ struct MyStructB : MyStructA { };
}
";

var callConv = "Cdecl";

if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && !Environment.Is64BitProcess)
{
callConv = "ThisCall";
}

var expectedOutputContents = $@"using System;
using System.Runtime.InteropServices;
Expand All @@ -511,7 +504,7 @@ public unsafe partial struct MyStructA
{{
public void** lpVtbl;
[UnmanagedFunctionPointer(CallingConvention.{callConv})]
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
public delegate void _MyMethod(MyStructA* pThis);
public void MyMethod()
Expand All @@ -528,7 +521,7 @@ public unsafe partial struct MyStructB
{{
public void** lpVtbl;
[UnmanagedFunctionPointer(CallingConvention.{callConv})]
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
public delegate void _MyMethod(MyStructB* pThis);
public void MyMethod()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using Xunit;

namespace ClangSharp.UnitTests
{
Expand Down Expand Up @@ -1301,6 +1302,59 @@ public partial struct MyStruct
return ValidateGeneratedCSharpCompatibleUnixBindingsAsync(inputContents, expectedOutputContents);
}

public override Task PackTest()
{
const string InputContents = @"struct MyStruct1 {
unsigned Field1;
void* Field2;
unsigned Field3;
};
#pragma pack(4)
struct MyStruct2 {
unsigned Field1;
void* Field2;
unsigned Field3;
};
";

const string ExpectedOutputContents = @"using System.Runtime.InteropServices;
namespace ClangSharp.Test
{
public unsafe partial struct MyStruct1
{
[NativeTypeName(""unsigned int"")]
public uint Field1;
public void* Field2;
[NativeTypeName(""unsigned int"")]
public uint Field3;
}
[StructLayout(LayoutKind.Sequential, Pack = 4)]
public unsafe partial struct MyStruct2
{
[NativeTypeName(""unsigned int"")]
public uint Field1;
public void* Field2;
[NativeTypeName(""unsigned int"")]
public uint Field3;
}
}
";

return ValidateGeneratedCSharpCompatibleUnixBindingsAsync(InputContents, ExpectedOutputContents);
}

public override Task PointerToSelfTest()
{
var inputContents = @"struct example_s {
Expand Down
Loading

0 comments on commit 4f7af5d

Please sign in to comment.