Skip to content

Add support for InlineArray, generate readonly instance members, and fix signed bitfield handling #464

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

Merged
merged 3 commits into from
Jul 8, 2023
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 @@ -61,7 +61,7 @@ internal partial interface IOutputBuilder
CSharpOutputBuilder BeginCSharpCode();
void EndCSharpCode(CSharpOutputBuilder output);

void BeginGetter(bool aggressivelyInlined);
void BeginGetter(bool aggressivelyInlined, bool isReadOnly);
void EndGetter();
void BeginSetter(bool aggressivelyInlined);
void EndSetter();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ public void WriteCustomAttribute(string attribute, Action? callback = null)
{
AddUsingDirective("System.Diagnostics.CodeAnalysis");
}
else if (attribute.StartsWith("InlineArray("))
{
AddUsingDirective("System.Runtime.CompilerServices");
}
else if (attribute.StartsWith("Guid(")|| attribute.Equals("Optional") || attribute.StartsWith("Optional, DefaultParameterValue("))
{
AddUsingDirective("System.Runtime.InteropServices");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ public void BeginValue(in ValueDesc desc)
{
WriteNewline();
WriteBlockStart();
BeginGetter(desc.IsConstant && _config.GenerateAggressiveInlining);
BeginGetter(desc.IsConstant && _config.GenerateAggressiveInlining, isReadOnly: false);
}
else
{
Expand Down Expand Up @@ -883,15 +883,15 @@ public void EndCSharpCode(CSharpOutputBuilder output)
// nop, used only by XML
}

public void BeginGetter(bool aggressivelyInlined)
public void BeginGetter(bool aggressivelyInlined, bool isReadOnly)
{
if (aggressivelyInlined)
{
AddUsingDirective("System.Runtime.CompilerServices");
WriteIndentedLine("[MethodImpl(MethodImplOptions.AggressiveInlining)]");
}

WriteIndentedLine("get");
WriteIndentedLine(isReadOnly ? "readonly get" : "get");
WriteBlockStart();
}

Expand Down
102 changes: 78 additions & 24 deletions sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -995,7 +995,7 @@ private void VisitIndirectFieldDecl(IndirectFieldDecl indirectFieldDecl)
var isIndirectPointerField = IsTypePointerOrReference(indirectFieldDecl, type) && (typeName != "IntPtr") && (typeName != "UIntPtr");

_outputBuilder.BeginBody();
_outputBuilder.BeginGetter(_config.GenerateAggressiveInlining);
_outputBuilder.BeginGetter(_config.GenerateAggressiveInlining, isReadOnly: fieldDecl.IsBitField && !Config.GenerateCompatibleCode);
var code = _outputBuilder.BeginCSharpCode();

if (fieldDecl.IsBitField)
Expand Down Expand Up @@ -2439,7 +2439,8 @@ void VisitBitfieldDecl(FieldDecl fieldDecl, BitfieldDesc[] bitfieldDescs, Record
bitfieldName += index.ToString();
}

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

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

var isTypeBackingSigned = false;

switch (builtinTypeBacking.Kind)
{
case CXType_Char_U:
Expand Down Expand Up @@ -2489,21 +2492,25 @@ void VisitBitfieldDecl(FieldDecl fieldDecl, BitfieldDesc[] bitfieldDescs, Record
case CXType_Short:
case CXType_Int:
{
isTypeBackingSigned = true;
break;
}

case CXType_Long:
{
isTypeBackingSigned = true;

if (_config.GenerateUnixTypes)
{
goto default;
}

goto case CXType_Int;
break;
}

case CXType_LongLong:
{
isTypeBackingSigned = true;

if (typeNameBacking == "nint")
{
goto case CXType_Int;
Expand Down Expand Up @@ -2545,6 +2552,8 @@ void VisitBitfieldDecl(FieldDecl fieldDecl, BitfieldDesc[] bitfieldDescs, Record
return;
}

var isTypeSigned = false;

switch (builtinType.Kind)
{
case CXType_Char_U:
Expand Down Expand Up @@ -2582,21 +2591,25 @@ void VisitBitfieldDecl(FieldDecl fieldDecl, BitfieldDesc[] bitfieldDescs, Record
case CXType_Short:
case CXType_Int:
{
isTypeSigned = true;
break;
}

case CXType_Long:
{
isTypeSigned = true;

if (_config.GenerateUnixTypes)
{
goto default;
}

goto case CXType_Int;
break;
}

case CXType_LongLong:
{
isTypeSigned = true;

if (typeNameBacking == "nint")
{
goto case CXType_Int;
Expand Down Expand Up @@ -2639,15 +2652,30 @@ void VisitBitfieldDecl(FieldDecl fieldDecl, BitfieldDesc[] bitfieldDescs, Record
_outputBuilder.BeginField(in desc);
_outputBuilder.WriteRegularField(typeName, escapedName);
_outputBuilder.BeginBody();
_outputBuilder.BeginGetter(_config.GenerateAggressiveInlining);
_outputBuilder.BeginGetter(_config.GenerateAggressiveInlining, isReadOnly: !Config.GenerateCompatibleCode);
var code = _outputBuilder.BeginCSharpCode();

code.WriteIndented("return ");

var recordDeclName = GetCursorName(recordDecl);

var isSmallType = currentSize < 4;
var isRemappedToSelf = _config.RemappedNames.TryGetValue(typeName, out var remappedTypeName) && typeName.Equals(remappedTypeName);
var needsCast = (currentSize < 4) || (type != builtinTypeBacking) || isRemappedToSelf;
var isTypeMismatch = type != builtinTypeBacking;
var isUnsignedToSigned = !isTypeBackingSigned && isTypeSigned;

var needsCast = isSmallType || isRemappedToSelf || isTypeMismatch || isUnsignedToSigned;
var needsParenFirst = !isSmallType && isUnsignedToSigned;
var needsParenSecond = !needsParenFirst || isRemappedToSelf;

// backing int, current int (value << cns) >> cns
// backing int, current uint (uint)((value >> cns) & msk)

// backing uint, current int ((int)value << cns) >> cns
// backing uint, current uint (value >> cns) & msk

// backing uint, current byte (byte)((value >> cns) & msk)
// backing uint, current sbyte (sbyte)((value << cns) >> cns)

if (needsCast)
{
Expand All @@ -2658,7 +2686,7 @@ void VisitBitfieldDecl(FieldDecl fieldDecl, BitfieldDesc[] bitfieldDescs, Record
code.Write(")(");
}

if (bitfieldOffset != 0)
if ((!needsParenFirst && (bitfieldOffset != 0)) || (!needsCast && isTypeSigned))
{
code.Write('(');
}
Expand All @@ -2675,21 +2703,37 @@ void VisitBitfieldDecl(FieldDecl fieldDecl, BitfieldDesc[] bitfieldDescs, Record
code.Write(bitfieldName);
code.EndMarker("bitfieldName");

if (bitfieldOffset != 0)
if (isTypeSigned)
{
code.Write(" >> ");
code.BeginMarker("bitfieldOffset");
code.Write(bitfieldOffset);
code.EndMarker("bitfieldOffset");
code.Write(" << ");
code.BeginMarker("remainingBitsMinusBitWidth");
code.Write(remainingBits - fieldDecl.BitWidthValue);
code.EndMarker("remainingBitsMinusBitWidth");
code.Write(')');

code.Write(" >> ");
code.BeginMarker("currentSizeMinusBitWidth");
code.Write((currentSize * 8) - fieldDecl.BitWidthValue);
code.EndMarker("currentSizeMinusBitWidth");
}
else
{
if (bitfieldOffset != 0)
{
code.Write(" >> ");
code.BeginMarker("bitfieldOffset");
code.Write(bitfieldOffset);
code.EndMarker("bitfieldOffset");
code.Write(')');
}

code.Write(" & 0x");
code.BeginMarker("bitwidthHexStringBacking");
code.Write(bitwidthHexStringBacking);
code.EndMarker("bitwidthHexStringBacking");
code.Write(" & 0x");
code.BeginMarker("bitwidthHexStringBacking");
code.Write(bitwidthHexStringBacking);
code.EndMarker("bitwidthHexStringBacking");
}

if (needsCast)
if (needsCast && needsParenSecond)
{
code.Write(')');
}
Expand Down Expand Up @@ -2868,6 +2912,11 @@ void VisitConstantOrIncompleteArrayFieldDecl(RecordDecl recordDecl, FieldDecl co
AddDiagnostic(DiagnosticLevel.Info, $"{escapedName} (constant array field) has a size of 0", constantOrIncompleteArray);
}

if (!_config.GeneratePreviewCode || (totalSize <= 1) || isUnsafeElementType)
{
totalSizeString = null;
}

var desc = new StructDesc {
AccessSpecifier = accessSpecifier,
EscapedName = escapedName,
Expand All @@ -2884,18 +2933,23 @@ void VisitConstantOrIncompleteArrayFieldDecl(RecordDecl recordDecl, FieldDecl co
Location = constantOrIncompleteArray.Location,
IsNested = true,
WriteCustomAttrs = static context => {
(var fieldDecl, var outputBuilder, var generator, var totalSizeString) = ((FieldDecl, IOutputBuilder, PInvokeGenerator, string))context;
(var fieldDecl, var outputBuilder, var generator, var totalSizeString) = ((FieldDecl, IOutputBuilder, PInvokeGenerator, string?))context;

generator.WithAttributes(fieldDecl);
generator.WithUsings(fieldDecl);

if (totalSizeString is not null)
{
outputBuilder.WriteCustomAttribute($"InlineArray({totalSizeString})");
}
},
CustomAttrGeneratorData = (constantOrIncompleteArray, _outputBuilder, this, totalSizeString),
};

_outputBuilder.BeginStruct(in desc);

var firstFieldName = "";
var numFieldsToEmit = totalSize;
var numFieldsToEmit = (totalSizeString is not null) ? Math.Min(totalSize, 1) : totalSize;

for (long i = 0; i < numFieldsToEmit; i++)
{
Expand Down Expand Up @@ -2963,7 +3017,7 @@ void VisitConstantOrIncompleteArrayFieldDecl(RecordDecl recordDecl, FieldDecl co
_outputBuilder.EndIndexerParameters();
_outputBuilder.BeginBody();

_outputBuilder.BeginGetter(_config.GenerateAggressiveInlining);
_outputBuilder.BeginGetter(_config.GenerateAggressiveInlining, isReadOnly: false);
var code = _outputBuilder.BeginCSharpCode();

code.WriteIndented("fixed (");
Expand All @@ -2982,7 +3036,7 @@ void VisitConstantOrIncompleteArrayFieldDecl(RecordDecl recordDecl, FieldDecl co
_outputBuilder.EndBody();
_outputBuilder.EndIndexer();
}
else
else if (totalSizeString is null)
{
_outputBuilder.BeginIndexer(AccessSpecifier.Public, isUnsafe: false, needsUnscopedRef: _config.GenerateLatestCode);
_outputBuilder.WriteIndexer($"ref {arrayTypeName}");
Expand All @@ -2996,7 +3050,7 @@ void VisitConstantOrIncompleteArrayFieldDecl(RecordDecl recordDecl, FieldDecl co
_outputBuilder.EndIndexerParameters();
_outputBuilder.BeginBody();

_outputBuilder.BeginGetter(_config.GenerateAggressiveInlining);
_outputBuilder.BeginGetter(_config.GenerateAggressiveInlining, isReadOnly: false);
var code = _outputBuilder.BeginCSharpCode();
code.AddUsingDirective("System");
code.AddUsingDirective("System.Runtime.InteropServices");
Expand Down
15 changes: 8 additions & 7 deletions sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2296,7 +2296,8 @@ private BitfieldDesc[] GetBitfieldDescs(RecordDecl recordDecl)
continue;
}

var currentSize = fieldDecl.Type.Handle.SizeOf;
var type = fieldDecl.Type;
var currentSize = type.Handle.SizeOf;

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

var type = fieldDecl.Type;
var typeBacking = type;

if (IsType<EnumType>(fieldDecl, type, out var enumType))
{
type = enumType.Decl.IntegerType;
typeBacking = enumType.Decl.IntegerType;
}

var bitfieldDesc = new BitfieldDesc {
TypeBacking = type,
TypeBacking = typeBacking,
Regions = new List<BitfieldRegion>() {
new BitfieldRegion {
Name = GetRemappedCursorName(fieldDecl),
Expand All @@ -2333,15 +2334,15 @@ private BitfieldDesc[] GetBitfieldDescs(RecordDecl recordDecl)
remainingBits += (currentSize - previousSize) * 8;
currentBits += (currentSize - previousSize) * 8;

var type = fieldDecl.Type;
var typeBacking = type;

if (IsType<EnumType>(fieldDecl, type, out var enumType))
{
type = enumType.Decl.IntegerType;
typeBacking = enumType.Decl.IntegerType;
}

bitfieldDescs[^1] = new BitfieldDesc {
TypeBacking = type,
TypeBacking = typeBacking,
Regions = bitfieldDesc.Regions,
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -332,9 +332,10 @@ public void EndCSharpCode(CSharpOutputBuilder output)
_ = _sb.Append("</code>");
}

public void BeginGetter(bool aggressivelyInlined)
public void BeginGetter(bool aggressivelyInlined, bool isReadOnly)
{
_ = _sb.Append("<get");

if (aggressivelyInlined)
{
_ = _sb.Append(" inlining=\"aggressive\"");
Expand Down
Loading