Skip to content

Commit 700c805

Browse files
Ensure that the record packing and calling convention are correct regardless of -m32 or -m64 (#258)
* Ensure that the record packing and calling convention are correct regardless of -m32 or -m64 * Disable the macOS legs
1 parent 69bf841 commit 700c805

39 files changed

+603
-454
lines changed

scripts/azure-pipelines.yml

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -61,17 +61,3 @@ jobs:
6161
configuration: Release
6262
architecture: x64
6363
OVERRIDE_RUNTIME_IDENTIFIER: ubuntu.18.04-x64
64-
65-
- template: azure-unix.yml
66-
parameters:
67-
name: macos_debug_x64
68-
pool: macOS-latest
69-
configuration: Debug
70-
architecture: x64
71-
72-
- template: azure-unix.yml
73-
parameters:
74-
name: macos_release_x64
75-
pool: macOS-latest
76-
configuration: Release
77-
architecture: x64

sources/ClangSharp.PInvokeGenerator/Abstractions/StructDesc.cs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,19 +63,21 @@ public StructLayoutAttribute LayoutAttribute
6363
{
6464
Debug.Assert(layout.Kind == LayoutKind.Explicit);
6565

66-
StructLayoutAttribute attribute = new(layout.Kind);
66+
var attribute = new StructLayoutAttribute(layout.Kind);
6767

68-
if (layout.Pack < layout.MaxFieldAlignment)
68+
if (layout.Pack != 0)
6969
{
7070
attribute.Pack = (int)layout.Pack;
7171
}
7272

7373
return attribute;
7474
}
7575

76-
if (layout.Pack < layout.MaxFieldAlignment)
76+
if (layout.Pack != 0)
7777
{
78-
return new StructLayoutAttribute(layout.Kind) {Pack = (int)layout.Pack};
78+
return new StructLayoutAttribute(layout.Kind) {
79+
Pack = (int)layout.Pack
80+
};
7981
}
8082

8183
return null;

sources/ClangSharp.PInvokeGenerator/CSharp/CSharpOutputBuilder.VisitDecl.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -433,15 +433,16 @@ public void EndFunctionOrDelegate(bool isVirtual, bool isBodyless)
433433

434434
public void BeginStruct<TCustomAttrGeneratorData>(in StructDesc<TCustomAttrGeneratorData> info)
435435
{
436-
if (info.LayoutAttribute is { } attribute)
436+
if (info.LayoutAttribute is not null)
437437
{
438438
AddUsingDirective("System.Runtime.InteropServices");
439439
WriteIndented("[StructLayout(LayoutKind.");
440-
Write(attribute.Value);
441-
if (attribute.Pack != default)
440+
Write(info.LayoutAttribute.Value);
441+
442+
if (info.LayoutAttribute.Pack != 0)
442443
{
443444
Write(", Pack = ");
444-
Write(attribute.Pack);
445+
Write(info.LayoutAttribute.Pack);
445446
}
446447

447448
WriteLine(")]");

sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -455,11 +455,11 @@ private void VisitFunctionDecl(FunctionDecl functionDecl)
455455
var callingConventionName = GetCallingConvention(functionDecl, cxxRecordDecl, type);
456456

457457
var isDllImport = body is null && !isVirtual;
458-
var entryPoint = isDllImport ? functionDecl.Handle.Mangling.CString : null;
458+
var entryPoint = "";
459459

460-
if (entryPoint == $"_{functionDecl.Name}")
460+
if (isDllImport)
461461
{
462-
entryPoint = functionDecl.Name;
462+
entryPoint = functionDecl.IsExternC ? GetCursorName(functionDecl) : functionDecl.Handle.Mangling.CString;
463463
}
464464

465465
var needsReturnFixup = isVirtual && NeedsReturnFixup(cxxMethodDecl);
@@ -1151,7 +1151,7 @@ private void VisitRecordDecl(RecordDecl recordDecl)
11511151
Alignment64 = alignment64,
11521152
Size32 = size32,
11531153
Size64 = size64,
1154-
Pack = alignment,
1154+
Pack = recordDecl.HasAttrs && recordDecl.Attrs.Any((attr) => attr.Kind == CX_AttrKind.CX_AttrKind_MaxFieldAlignment) && ((alignment != alignment32) || (alignment != alignment64)) ? alignment : 0,
11551155
MaxFieldAlignment = maxAlignm,
11561156
Kind = layoutKind
11571157
},
@@ -2201,7 +2201,7 @@ void VisitConstantArrayFieldDecl(RecordDecl recordDecl, FieldDecl constantArray)
22012201
Alignment64 = alignment64,
22022202
Size32 = size32,
22032203
Size64 = size64,
2204-
Pack = alignment,
2204+
Pack = recordDecl.HasAttrs && recordDecl.Attrs.Any((attr) => attr.Kind == CX_AttrKind.CX_AttrKind_MaxFieldAlignment) && ((alignment != alignment32) || (alignment != alignment64)) ? alignment : 0,
22052205
MaxFieldAlignment = maxAlignm,
22062206
Kind = LayoutKind.Sequential
22072207
},

sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -877,6 +877,10 @@ private CallingConvention GetCallingConvention(Cursor cursor, Cursor context, Ty
877877
{
878878
case CXCallingConv.CXCallingConv_C:
879879
{
880+
if ((cursor is CXXMethodDecl cxxMethodDecl) && cxxMethodDecl.IsInstance)
881+
{
882+
return CallingConvention.ThisCall;
883+
}
880884
return CallingConvention.Cdecl;
881885
}
882886

sources/ClangSharp.PInvokeGenerator/XML/XmlOutputBuilder.VisitDecl.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -248,15 +248,16 @@ public void BeginStruct<TCustomAttrGeneratorData>(in StructDesc<TCustomAttrGener
248248
_ = _sb.Append(" unsafe=\"true\"");
249249
}
250250

251-
if (info.LayoutAttribute is { } attribute)
251+
if (info.LayoutAttribute is not null)
252252
{
253253
_ = _sb.Append(" layout=\"");
254-
_ = _sb.Append(attribute.Value);
254+
_ = _sb.Append(info.LayoutAttribute.Value);
255255
_ = _sb.Append('"');
256-
if (attribute.Pack != default)
256+
257+
if (info.LayoutAttribute.Pack != 0)
257258
{
258259
_ = _sb.Append(" pack=\"");
259-
_ = _sb.Append(attribute.Pack);
260+
_ = _sb.Append(info.LayoutAttribute.Pack);
260261
_ = _sb.Append('"');
261262
}
262263
}

tests/ClangSharp.PInvokeGenerator.UnitTests/Base/StructDeclarationTest.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,9 @@ public abstract class StructDeclarationTest : PInvokeGeneratorTest
153153
[Fact]
154154
public abstract Task NoDefinitionTest();
155155

156+
[Fact]
157+
public abstract Task PackTest();
158+
156159
[Fact]
157160
public abstract Task PointerToSelfTest();
158161

tests/ClangSharp.PInvokeGenerator.UnitTests/CSharpCompatibleUnix/CXXMethodDeclarationTest.cs

Lines changed: 15 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -164,14 +164,12 @@ int MyInt32Method()
164164
}
165165
};
166166
";
167-
var callConv = "Cdecl";
168167
var entryPoint = RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? "__ZN8MyStruct12MyVoidMethodEv" : "_ZN8MyStruct12MyVoidMethodEv";
169168

170169
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
171170
{
172171
if (!Environment.Is64BitProcess)
173172
{
174-
callConv = "ThisCall";
175173
entryPoint = "?MyVoidMethod@MyStruct@@QAEXXZ";
176174
}
177175
else
@@ -186,7 +184,7 @@ namespace ClangSharp.Test
186184
{{
187185
public partial struct MyStruct
188186
{{
189-
[DllImport(""ClangSharpPInvokeGenerator"", CallingConvention = CallingConvention.{callConv}, EntryPoint = ""{entryPoint}"", ExactSpelling = true)]
187+
[DllImport(""ClangSharpPInvokeGenerator"", CallingConvention = CallingConvention.ThisCall, EntryPoint = ""{entryPoint}"", ExactSpelling = true)]
190188
public static extern void MyVoidMethod();
191189
192190
public int MyInt32Method()
@@ -417,13 +415,6 @@ public override Task NewKeywordVirtualTest()
417415
virtual int GetType(int objA, int objB) = 0;
418416
};";
419417

420-
var callConv = "Cdecl";
421-
422-
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && !Environment.Is64BitProcess)
423-
{
424-
callConv = "ThisCall";
425-
}
426-
427418
var expectedOutputContents = $@"using System;
428419
using System.Runtime.InteropServices;
429420
@@ -433,13 +424,13 @@ public unsafe partial struct MyStruct
433424
{{
434425
public void** lpVtbl;
435426
436-
[UnmanagedFunctionPointer(CallingConvention.{callConv})]
427+
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
437428
public delegate int _GetType(MyStruct* pThis, int obj);
438429
439-
[UnmanagedFunctionPointer(CallingConvention.{callConv})]
430+
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
440431
public delegate int _GetType1(MyStruct* pThis);
441432
442-
[UnmanagedFunctionPointer(CallingConvention.{callConv})]
433+
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
443434
public delegate int _GetType2(MyStruct* pThis, int objA, int objB);
444435
445436
public int GetType(int obj)
@@ -481,12 +472,10 @@ public override Task NewKeywordVirtualWithExplicitVtblTest()
481472
virtual int GetType(int objA, int objB) = 0;
482473
};";
483474

484-
var callConv = "Cdecl";
485475
var nativeCallConv = "";
486476

487477
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && !Environment.Is64BitProcess)
488478
{
489-
callConv = "ThisCall";
490479
nativeCallConv = " __attribute__((thiscall))";
491480
}
492481

@@ -499,13 +488,13 @@ public unsafe partial struct MyStruct
499488
{{
500489
public Vtbl* lpVtbl;
501490
502-
[UnmanagedFunctionPointer(CallingConvention.{callConv})]
491+
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
503492
public delegate int _GetType(MyStruct* pThis, int obj);
504493
505-
[UnmanagedFunctionPointer(CallingConvention.{callConv})]
494+
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
506495
public delegate int _GetType1(MyStruct* pThis);
507496
508-
[UnmanagedFunctionPointer(CallingConvention.{callConv})]
497+
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
509498
public delegate int _GetType2(MyStruct* pThis, int objA, int objB);
510499
511500
public int GetType(int obj)
@@ -806,13 +795,6 @@ virtual char MyInt8Method()
806795
};
807796
";
808797

809-
var callConv = "Cdecl";
810-
811-
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && !Environment.Is64BitProcess)
812-
{
813-
callConv = "ThisCall";
814-
}
815-
816798
var expectedOutputContents = $@"using System;
817799
using System.Runtime.InteropServices;
818800
@@ -822,17 +804,17 @@ public unsafe partial struct MyStruct
822804
{{
823805
public void** lpVtbl;
824806
825-
[UnmanagedFunctionPointer(CallingConvention.{callConv})]
807+
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
826808
public delegate void _MyVoidMethod(MyStruct* pThis);
827809
828-
[UnmanagedFunctionPointer(CallingConvention.{callConv})]
810+
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
829811
[return: NativeTypeName(""char"")]
830812
public delegate sbyte _MyInt8Method(MyStruct* pThis);
831813
832-
[UnmanagedFunctionPointer(CallingConvention.{callConv})]
814+
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
833815
public delegate int _MyInt32Method(MyStruct* pThis);
834816
835-
[UnmanagedFunctionPointer(CallingConvention.{callConv})]
817+
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
836818
public delegate void* _MyVoidStarMethod(MyStruct* pThis);
837819
838820
public void MyVoidMethod()
@@ -891,13 +873,6 @@ virtual char MyInt8Method()
891873
};
892874
";
893875

894-
var callConv = "Cdecl";
895-
896-
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && !Environment.Is64BitProcess)
897-
{
898-
callConv = "ThisCall";
899-
}
900-
901876
var expectedOutputContents = $@"using System;
902877
using System.Runtime.InteropServices;
903878
@@ -907,17 +882,17 @@ public unsafe partial struct MyStruct
907882
{{
908883
public void** lpVtbl;
909884
910-
[UnmanagedFunctionPointer(CallingConvention.{callConv})]
885+
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
911886
public delegate void _MyVoidMethod(MyStruct* pThis);
912887
913-
[UnmanagedFunctionPointer(CallingConvention.{callConv})]
888+
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
914889
[return: NativeTypeName(""char"")]
915890
public delegate sbyte _MyInt8Method(MyStruct* pThis);
916891
917-
[UnmanagedFunctionPointer(CallingConvention.{callConv})]
892+
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
918893
public delegate int _MyInt32Method(MyStruct* pThis);
919894
920-
[UnmanagedFunctionPointer(CallingConvention.{callConv})]
895+
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
921896
public delegate void* _MyVoidStarMethod(MyStruct* pThis);
922897
923898
[VtblIndex(0)]

tests/ClangSharp.PInvokeGenerator.UnitTests/CSharpCompatibleUnix/FunctionDeclarationBodyImportTest.cs

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -495,13 +495,6 @@ struct MyStructB : MyStructA { };
495495
}
496496
";
497497

498-
var callConv = "Cdecl";
499-
500-
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && !Environment.Is64BitProcess)
501-
{
502-
callConv = "ThisCall";
503-
}
504-
505498
var expectedOutputContents = $@"using System;
506499
using System.Runtime.InteropServices;
507500
@@ -511,7 +504,7 @@ public unsafe partial struct MyStructA
511504
{{
512505
public void** lpVtbl;
513506
514-
[UnmanagedFunctionPointer(CallingConvention.{callConv})]
507+
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
515508
public delegate void _MyMethod(MyStructA* pThis);
516509
517510
public void MyMethod()
@@ -528,7 +521,7 @@ public unsafe partial struct MyStructB
528521
{{
529522
public void** lpVtbl;
530523
531-
[UnmanagedFunctionPointer(CallingConvention.{callConv})]
524+
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
532525
public delegate void _MyMethod(MyStructB* pThis);
533526
534527
public void MyMethod()

tests/ClangSharp.PInvokeGenerator.UnitTests/CSharpCompatibleUnix/StructDeclarationTest.cs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System.Collections.Generic;
55
using System.Runtime.InteropServices;
66
using System.Threading.Tasks;
7+
using Xunit;
78

89
namespace ClangSharp.UnitTests
910
{
@@ -1301,6 +1302,59 @@ public partial struct MyStruct
13011302
return ValidateGeneratedCSharpCompatibleUnixBindingsAsync(inputContents, expectedOutputContents);
13021303
}
13031304

1305+
public override Task PackTest()
1306+
{
1307+
const string InputContents = @"struct MyStruct1 {
1308+
unsigned Field1;
1309+
1310+
void* Field2;
1311+
1312+
unsigned Field3;
1313+
};
1314+
1315+
#pragma pack(4)
1316+
1317+
struct MyStruct2 {
1318+
unsigned Field1;
1319+
1320+
void* Field2;
1321+
1322+
unsigned Field3;
1323+
};
1324+
";
1325+
1326+
const string ExpectedOutputContents = @"using System.Runtime.InteropServices;
1327+
1328+
namespace ClangSharp.Test
1329+
{
1330+
public unsafe partial struct MyStruct1
1331+
{
1332+
[NativeTypeName(""unsigned int"")]
1333+
public uint Field1;
1334+
1335+
public void* Field2;
1336+
1337+
[NativeTypeName(""unsigned int"")]
1338+
public uint Field3;
1339+
}
1340+
1341+
[StructLayout(LayoutKind.Sequential, Pack = 4)]
1342+
public unsafe partial struct MyStruct2
1343+
{
1344+
[NativeTypeName(""unsigned int"")]
1345+
public uint Field1;
1346+
1347+
public void* Field2;
1348+
1349+
[NativeTypeName(""unsigned int"")]
1350+
public uint Field3;
1351+
}
1352+
}
1353+
";
1354+
1355+
return ValidateGeneratedCSharpCompatibleUnixBindingsAsync(InputContents, ExpectedOutputContents);
1356+
}
1357+
13041358
public override Task PointerToSelfTest()
13051359
{
13061360
var inputContents = @"struct example_s {

0 commit comments

Comments
 (0)