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

Ensure that the record packing and calling convention are correct regardless of -m32 or -m64 #258

Merged
merged 2 commits into from
Aug 4, 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
14 changes: 0 additions & 14 deletions scripts/azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,17 +61,3 @@ jobs:
configuration: Release
architecture: x64
OVERRIDE_RUNTIME_IDENTIFIER: ubuntu.18.04-x64

- template: azure-unix.yml
parameters:
name: macos_debug_x64
pool: macOS-latest
configuration: Debug
architecture: x64

- template: azure-unix.yml
parameters:
name: macos_release_x64
pool: macOS-latest
configuration: Release
architecture: x64
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
4 changes: 4 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
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 @@ -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