Skip to content

Commit 641a0d2

Browse files
Merge pull request #464 from tannergooding/main
Add support for InlineArray, generate readonly instance members, and fix signed bitfield handling
2 parents 646034e + ac41505 commit 641a0d2

38 files changed

+604
-1512
lines changed

sources/ClangSharp.PInvokeGenerator/Abstractions/IOutputBuilder.VisitDecl.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ internal partial interface IOutputBuilder
6161
CSharpOutputBuilder BeginCSharpCode();
6262
void EndCSharpCode(CSharpOutputBuilder output);
6363

64-
void BeginGetter(bool aggressivelyInlined);
64+
void BeginGetter(bool aggressivelyInlined, bool isReadOnly);
6565
void EndGetter();
6666
void BeginSetter(bool aggressivelyInlined);
6767
void EndSetter();

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ public void WriteCustomAttribute(string attribute, Action? callback = null)
2222
{
2323
AddUsingDirective("System.Diagnostics.CodeAnalysis");
2424
}
25+
else if (attribute.StartsWith("InlineArray("))
26+
{
27+
AddUsingDirective("System.Runtime.CompilerServices");
28+
}
2529
else if (attribute.StartsWith("Guid(")|| attribute.Equals("Optional") || attribute.StartsWith("Optional, DefaultParameterValue("))
2630
{
2731
AddUsingDirective("System.Runtime.InteropServices");

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ public void BeginValue(in ValueDesc desc)
177177
{
178178
WriteNewline();
179179
WriteBlockStart();
180-
BeginGetter(desc.IsConstant && _config.GenerateAggressiveInlining);
180+
BeginGetter(desc.IsConstant && _config.GenerateAggressiveInlining, isReadOnly: false);
181181
}
182182
else
183183
{
@@ -883,15 +883,15 @@ public void EndCSharpCode(CSharpOutputBuilder output)
883883
// nop, used only by XML
884884
}
885885

886-
public void BeginGetter(bool aggressivelyInlined)
886+
public void BeginGetter(bool aggressivelyInlined, bool isReadOnly)
887887
{
888888
if (aggressivelyInlined)
889889
{
890890
AddUsingDirective("System.Runtime.CompilerServices");
891891
WriteIndentedLine("[MethodImpl(MethodImplOptions.AggressiveInlining)]");
892892
}
893893

894-
WriteIndentedLine("get");
894+
WriteIndentedLine(isReadOnly ? "readonly get" : "get");
895895
WriteBlockStart();
896896
}
897897

sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs

Lines changed: 78 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -995,7 +995,7 @@ private void VisitIndirectFieldDecl(IndirectFieldDecl indirectFieldDecl)
995995
var isIndirectPointerField = IsTypePointerOrReference(indirectFieldDecl, type) && (typeName != "IntPtr") && (typeName != "UIntPtr");
996996

997997
_outputBuilder.BeginBody();
998-
_outputBuilder.BeginGetter(_config.GenerateAggressiveInlining);
998+
_outputBuilder.BeginGetter(_config.GenerateAggressiveInlining, isReadOnly: fieldDecl.IsBitField && !Config.GenerateCompatibleCode);
999999
var code = _outputBuilder.BeginCSharpCode();
10001000

10011001
if (fieldDecl.IsBitField)
@@ -2439,7 +2439,8 @@ void VisitBitfieldDecl(FieldDecl fieldDecl, BitfieldDesc[] bitfieldDescs, Record
24392439
bitfieldName += index.ToString();
24402440
}
24412441

2442-
typeBacking = (index > 0) ? bitfieldDescs[index - 1].TypeBacking : bitfieldDescs[0].TypeBacking;
2442+
var bitfieldDesc = (index > 0) ? bitfieldDescs[index - 1] : bitfieldDescs[0];
2443+
typeBacking = bitfieldDesc.TypeBacking;
24432444
typeNameBacking = GetRemappedTypeName(fieldDecl, context: null, typeBacking, out _);
24442445
}
24452446

@@ -2452,6 +2453,8 @@ void VisitBitfieldDecl(FieldDecl fieldDecl, BitfieldDesc[] bitfieldDescs, Record
24522453
return;
24532454
}
24542455

2456+
var isTypeBackingSigned = false;
2457+
24552458
switch (builtinTypeBacking.Kind)
24562459
{
24572460
case CXType_Char_U:
@@ -2489,21 +2492,25 @@ void VisitBitfieldDecl(FieldDecl fieldDecl, BitfieldDesc[] bitfieldDescs, Record
24892492
case CXType_Short:
24902493
case CXType_Int:
24912494
{
2495+
isTypeBackingSigned = true;
24922496
break;
24932497
}
24942498

24952499
case CXType_Long:
24962500
{
2501+
isTypeBackingSigned = true;
2502+
24972503
if (_config.GenerateUnixTypes)
24982504
{
24992505
goto default;
25002506
}
2501-
2502-
goto case CXType_Int;
2507+
break;
25032508
}
25042509

25052510
case CXType_LongLong:
25062511
{
2512+
isTypeBackingSigned = true;
2513+
25072514
if (typeNameBacking == "nint")
25082515
{
25092516
goto case CXType_Int;
@@ -2545,6 +2552,8 @@ void VisitBitfieldDecl(FieldDecl fieldDecl, BitfieldDesc[] bitfieldDescs, Record
25452552
return;
25462553
}
25472554

2555+
var isTypeSigned = false;
2556+
25482557
switch (builtinType.Kind)
25492558
{
25502559
case CXType_Char_U:
@@ -2582,21 +2591,25 @@ void VisitBitfieldDecl(FieldDecl fieldDecl, BitfieldDesc[] bitfieldDescs, Record
25822591
case CXType_Short:
25832592
case CXType_Int:
25842593
{
2594+
isTypeSigned = true;
25852595
break;
25862596
}
25872597

25882598
case CXType_Long:
25892599
{
2600+
isTypeSigned = true;
2601+
25902602
if (_config.GenerateUnixTypes)
25912603
{
25922604
goto default;
25932605
}
2594-
2595-
goto case CXType_Int;
2606+
break;
25962607
}
25972608

25982609
case CXType_LongLong:
25992610
{
2611+
isTypeSigned = true;
2612+
26002613
if (typeNameBacking == "nint")
26012614
{
26022615
goto case CXType_Int;
@@ -2639,15 +2652,30 @@ void VisitBitfieldDecl(FieldDecl fieldDecl, BitfieldDesc[] bitfieldDescs, Record
26392652
_outputBuilder.BeginField(in desc);
26402653
_outputBuilder.WriteRegularField(typeName, escapedName);
26412654
_outputBuilder.BeginBody();
2642-
_outputBuilder.BeginGetter(_config.GenerateAggressiveInlining);
2655+
_outputBuilder.BeginGetter(_config.GenerateAggressiveInlining, isReadOnly: !Config.GenerateCompatibleCode);
26432656
var code = _outputBuilder.BeginCSharpCode();
26442657

26452658
code.WriteIndented("return ");
26462659

26472660
var recordDeclName = GetCursorName(recordDecl);
26482661

2662+
var isSmallType = currentSize < 4;
26492663
var isRemappedToSelf = _config.RemappedNames.TryGetValue(typeName, out var remappedTypeName) && typeName.Equals(remappedTypeName);
2650-
var needsCast = (currentSize < 4) || (type != builtinTypeBacking) || isRemappedToSelf;
2664+
var isTypeMismatch = type != builtinTypeBacking;
2665+
var isUnsignedToSigned = !isTypeBackingSigned && isTypeSigned;
2666+
2667+
var needsCast = isSmallType || isRemappedToSelf || isTypeMismatch || isUnsignedToSigned;
2668+
var needsParenFirst = !isSmallType && isUnsignedToSigned;
2669+
var needsParenSecond = !needsParenFirst || isRemappedToSelf;
2670+
2671+
// backing int, current int (value << cns) >> cns
2672+
// backing int, current uint (uint)((value >> cns) & msk)
2673+
2674+
// backing uint, current int ((int)value << cns) >> cns
2675+
// backing uint, current uint (value >> cns) & msk
2676+
2677+
// backing uint, current byte (byte)((value >> cns) & msk)
2678+
// backing uint, current sbyte (sbyte)((value << cns) >> cns)
26512679

26522680
if (needsCast)
26532681
{
@@ -2658,7 +2686,7 @@ void VisitBitfieldDecl(FieldDecl fieldDecl, BitfieldDesc[] bitfieldDescs, Record
26582686
code.Write(")(");
26592687
}
26602688

2661-
if (bitfieldOffset != 0)
2689+
if ((!needsParenFirst && (bitfieldOffset != 0)) || (!needsCast && isTypeSigned))
26622690
{
26632691
code.Write('(');
26642692
}
@@ -2675,21 +2703,37 @@ void VisitBitfieldDecl(FieldDecl fieldDecl, BitfieldDesc[] bitfieldDescs, Record
26752703
code.Write(bitfieldName);
26762704
code.EndMarker("bitfieldName");
26772705

2678-
if (bitfieldOffset != 0)
2706+
if (isTypeSigned)
26792707
{
2680-
code.Write(" >> ");
2681-
code.BeginMarker("bitfieldOffset");
2682-
code.Write(bitfieldOffset);
2683-
code.EndMarker("bitfieldOffset");
2708+
code.Write(" << ");
2709+
code.BeginMarker("remainingBitsMinusBitWidth");
2710+
code.Write(remainingBits - fieldDecl.BitWidthValue);
2711+
code.EndMarker("remainingBitsMinusBitWidth");
26842712
code.Write(')');
2713+
2714+
code.Write(" >> ");
2715+
code.BeginMarker("currentSizeMinusBitWidth");
2716+
code.Write((currentSize * 8) - fieldDecl.BitWidthValue);
2717+
code.EndMarker("currentSizeMinusBitWidth");
26852718
}
2719+
else
2720+
{
2721+
if (bitfieldOffset != 0)
2722+
{
2723+
code.Write(" >> ");
2724+
code.BeginMarker("bitfieldOffset");
2725+
code.Write(bitfieldOffset);
2726+
code.EndMarker("bitfieldOffset");
2727+
code.Write(')');
2728+
}
26862729

2687-
code.Write(" & 0x");
2688-
code.BeginMarker("bitwidthHexStringBacking");
2689-
code.Write(bitwidthHexStringBacking);
2690-
code.EndMarker("bitwidthHexStringBacking");
2730+
code.Write(" & 0x");
2731+
code.BeginMarker("bitwidthHexStringBacking");
2732+
code.Write(bitwidthHexStringBacking);
2733+
code.EndMarker("bitwidthHexStringBacking");
2734+
}
26912735

2692-
if (needsCast)
2736+
if (needsCast && needsParenSecond)
26932737
{
26942738
code.Write(')');
26952739
}
@@ -2868,6 +2912,11 @@ void VisitConstantOrIncompleteArrayFieldDecl(RecordDecl recordDecl, FieldDecl co
28682912
AddDiagnostic(DiagnosticLevel.Info, $"{escapedName} (constant array field) has a size of 0", constantOrIncompleteArray);
28692913
}
28702914

2915+
if (!_config.GeneratePreviewCode || (totalSize <= 1) || isUnsafeElementType)
2916+
{
2917+
totalSizeString = null;
2918+
}
2919+
28712920
var desc = new StructDesc {
28722921
AccessSpecifier = accessSpecifier,
28732922
EscapedName = escapedName,
@@ -2884,18 +2933,23 @@ void VisitConstantOrIncompleteArrayFieldDecl(RecordDecl recordDecl, FieldDecl co
28842933
Location = constantOrIncompleteArray.Location,
28852934
IsNested = true,
28862935
WriteCustomAttrs = static context => {
2887-
(var fieldDecl, var outputBuilder, var generator, var totalSizeString) = ((FieldDecl, IOutputBuilder, PInvokeGenerator, string))context;
2936+
(var fieldDecl, var outputBuilder, var generator, var totalSizeString) = ((FieldDecl, IOutputBuilder, PInvokeGenerator, string?))context;
28882937

28892938
generator.WithAttributes(fieldDecl);
28902939
generator.WithUsings(fieldDecl);
2940+
2941+
if (totalSizeString is not null)
2942+
{
2943+
outputBuilder.WriteCustomAttribute($"InlineArray({totalSizeString})");
2944+
}
28912945
},
28922946
CustomAttrGeneratorData = (constantOrIncompleteArray, _outputBuilder, this, totalSizeString),
28932947
};
28942948

28952949
_outputBuilder.BeginStruct(in desc);
28962950

28972951
var firstFieldName = "";
2898-
var numFieldsToEmit = totalSize;
2952+
var numFieldsToEmit = (totalSizeString is not null) ? Math.Min(totalSize, 1) : totalSize;
28992953

29002954
for (long i = 0; i < numFieldsToEmit; i++)
29012955
{
@@ -2963,7 +3017,7 @@ void VisitConstantOrIncompleteArrayFieldDecl(RecordDecl recordDecl, FieldDecl co
29633017
_outputBuilder.EndIndexerParameters();
29643018
_outputBuilder.BeginBody();
29653019

2966-
_outputBuilder.BeginGetter(_config.GenerateAggressiveInlining);
3020+
_outputBuilder.BeginGetter(_config.GenerateAggressiveInlining, isReadOnly: false);
29673021
var code = _outputBuilder.BeginCSharpCode();
29683022

29693023
code.WriteIndented("fixed (");
@@ -2982,7 +3036,7 @@ void VisitConstantOrIncompleteArrayFieldDecl(RecordDecl recordDecl, FieldDecl co
29823036
_outputBuilder.EndBody();
29833037
_outputBuilder.EndIndexer();
29843038
}
2985-
else
3039+
else if (totalSizeString is null)
29863040
{
29873041
_outputBuilder.BeginIndexer(AccessSpecifier.Public, isUnsafe: false, needsUnscopedRef: _config.GenerateLatestCode);
29883042
_outputBuilder.WriteIndexer($"ref {arrayTypeName}");
@@ -2996,7 +3050,7 @@ void VisitConstantOrIncompleteArrayFieldDecl(RecordDecl recordDecl, FieldDecl co
29963050
_outputBuilder.EndIndexerParameters();
29973051
_outputBuilder.BeginBody();
29983052

2999-
_outputBuilder.BeginGetter(_config.GenerateAggressiveInlining);
3053+
_outputBuilder.BeginGetter(_config.GenerateAggressiveInlining, isReadOnly: false);
30003054
var code = _outputBuilder.BeginCSharpCode();
30013055
code.AddUsingDirective("System");
30023056
code.AddUsingDirective("System.Runtime.InteropServices");

sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2296,7 +2296,8 @@ private BitfieldDesc[] GetBitfieldDescs(RecordDecl recordDecl)
22962296
continue;
22972297
}
22982298

2299-
var currentSize = fieldDecl.Type.Handle.SizeOf;
2299+
var type = fieldDecl.Type;
2300+
var currentSize = type.Handle.SizeOf;
23002301

23012302
if ((!_config.GenerateUnixTypes && (currentSize != previousSize)) || (fieldDecl.BitWidthValue > remainingBits))
23022303
{
@@ -2305,15 +2306,15 @@ private BitfieldDesc[] GetBitfieldDescs(RecordDecl recordDecl)
23052306
remainingBits = currentBits;
23062307
previousSize = 0;
23072308

2308-
var type = fieldDecl.Type;
2309+
var typeBacking = type;
23092310

23102311
if (IsType<EnumType>(fieldDecl, type, out var enumType))
23112312
{
2312-
type = enumType.Decl.IntegerType;
2313+
typeBacking = enumType.Decl.IntegerType;
23132314
}
23142315

23152316
var bitfieldDesc = new BitfieldDesc {
2316-
TypeBacking = type,
2317+
TypeBacking = typeBacking,
23172318
Regions = new List<BitfieldRegion>() {
23182319
new BitfieldRegion {
23192320
Name = GetRemappedCursorName(fieldDecl),
@@ -2333,15 +2334,15 @@ private BitfieldDesc[] GetBitfieldDescs(RecordDecl recordDecl)
23332334
remainingBits += (currentSize - previousSize) * 8;
23342335
currentBits += (currentSize - previousSize) * 8;
23352336

2336-
var type = fieldDecl.Type;
2337+
var typeBacking = type;
23372338

23382339
if (IsType<EnumType>(fieldDecl, type, out var enumType))
23392340
{
2340-
type = enumType.Decl.IntegerType;
2341+
typeBacking = enumType.Decl.IntegerType;
23412342
}
23422343

23432344
bitfieldDescs[^1] = new BitfieldDesc {
2344-
TypeBacking = type,
2345+
TypeBacking = typeBacking,
23452346
Regions = bitfieldDesc.Regions,
23462347
};
23472348
}

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -332,9 +332,10 @@ public void EndCSharpCode(CSharpOutputBuilder output)
332332
_ = _sb.Append("</code>");
333333
}
334334

335-
public void BeginGetter(bool aggressivelyInlined)
335+
public void BeginGetter(bool aggressivelyInlined, bool isReadOnly)
336336
{
337337
_ = _sb.Append("<get");
338+
338339
if (aggressivelyInlined)
339340
{
340341
_ = _sb.Append(" inlining=\"aggressive\"");

0 commit comments

Comments
 (0)