Skip to content
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
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ See [LICENSE.md](LICENSE.md) in the repository root for more information.

### Building Managed

ClangSharp requires the [.NET 8 SDK](https://dotnet.microsoft.com/download/dotnet/8.0) and can be built simply with `dotnet build -c Release`.
ClangSharp requires the [.NET 10 SDK](https://dotnet.microsoft.com/download/dotnet/10.0) and can be built simply with `dotnet build -c Release`.

You can reproduce what the CI environment does by running `./scripts/cibuild.cmd` on Windows or `./scripts.cibuild.sh` on Unix.
This will download the required .NET SDK locally and use that to build the repo; it will also run through all available actions in the appropriate order.
Expand Down Expand Up @@ -133,7 +133,7 @@ This program will take a given set of C or C++ header files and generate C# bind

The simplest and recommended setup is to install the generator as a .NET tool and then use response files:
```
dotnet tool install --global ClangSharpPInvokeGenerator --version 20.1.2
dotnet tool install --global ClangSharpPInvokeGenerator
ClangSharpPInvokeGenerator @generate.rsp
```

Expand Down Expand Up @@ -202,9 +202,9 @@ Options:
# Codegen Options

compatible-codegen Bindings should be generated with .NET Standard 2.0 compatibility. Setting this disables preview code generation.
default-codegen Bindings should be generated for the previous LTS version of .NET/C#. This is currently .NET 6/C# 10.
latest-codegen Bindings should be generated for the current LTS/STS version of .NET/C#. This is currently .NET 8/C# 12.
preview-codegen Bindings should be generated for the preview version of .NET/C#. This is currently .NET 9/C# 13.
default-codegen Bindings should be generated for the previous LTS version of .NET/C#. This is currently .NET 8/C# 12.
latest-codegen Bindings should be generated for the current LTS/STS version of .NET/C#. This is currently .NET 10/C# 14.
preview-codegen Bindings should be generated for the preview version of .NET/C#. This is currently .NET 10/C# 14.

# File Options

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,21 @@ readonly get
}
}

public bool IsReadOnly
{
readonly get
{
return (Flags & FunctionOrDelegateFlags.IsReadOnly) != 0;
}

set
{
Flags = value
? Flags | FunctionOrDelegateFlags.IsReadOnly
: Flags & ~FunctionOrDelegateFlags.IsReadOnly;
}
}

public bool HasFnPtrCodeGen
{
readonly get
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ public void WriteIid(string name, Guid value)

WriteIndented("ReadOnlySpan<byte> data = ");

if (_generator.Config.GenerateLatestCode)
if (!_generator.Config.GenerateCompatibleCode)
{
WriteLine('[');
}
Expand Down Expand Up @@ -124,7 +124,7 @@ public void WriteIid(string name, Guid value)
WriteNewline();
DecreaseIndentation();

if (_generator.Config.GenerateLatestCode)
if (!_generator.Config.GenerateCompatibleCode)
{
WriteIndented(']');
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -517,6 +517,11 @@ public void BeginFunctionOrDelegate(in FunctionOrDelegateDesc desc, ref bool isM
Write("new ");
}

if (desc.IsReadOnly)
{
Write("readonly ");
}

if (desc.IsUnsafe)
{
if (!desc.IsCtxCxxRecord)
Expand Down Expand Up @@ -808,7 +813,7 @@ public void BeginStruct(in StructDesc desc)
Write(".Interface");
}

if ((desc.Uuid is not null) && _generator.Config.GenerateGuidMember && _generator.Config.GenerateLatestCode)
if ((desc.Uuid is not null) && _generator.Config.GenerateGuidMember && !_generator.Config.GenerateCompatibleCode)
{
Write(desc.HasVtbl ? ", " : " : ");
Write("INativeGuid");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,5 @@ internal enum FunctionOrDelegateFlags
NeedsReturnFixup = 1 << 13,
IsCxxConstructor = 1 << 14,
IsManualImport = 1 << 15,
IsReadOnly = 1 << 16,
}
54 changes: 45 additions & 9 deletions sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -607,6 +607,7 @@ private void VisitFunctionDecl(FunctionDecl functionDecl)
IsCxx = cxxMethodDecl is not null,
IsStatic = isDllImport || (cxxMethodDecl is null) || cxxMethodDecl.IsStatic,
NeedsNewKeyword = NeedsNewKeyword(escapedName, functionDecl.Parameters),
IsReadOnly = (cxxMethodDecl is not null) && cxxMethodDecl.IsConst,
IsUnsafe = IsUnsafe(functionDecl),
IsCtxCxxRecord = cxxRecordDecl is not null,
IsCxxRecordCtxUnsafe = cxxRecordDecl is not null && IsUnsafe(cxxRecordDecl),
Expand Down Expand Up @@ -944,7 +945,7 @@ private void VisitIndirectFieldDecl(IndirectFieldDecl indirectFieldDecl)
ParentName = GetRemappedCursorName(parent),
Offset = null,
NeedsNewKeyword = false,
NeedsUnscopedRef = _config.GenerateLatestCode && !fieldDecl.IsBitField,
NeedsUnscopedRef = !_config.GenerateCompatibleCode && !fieldDecl.IsBitField,
Location = fieldDecl.Location,
HasBody = true,
WriteCustomAttrs = static context => {
Expand Down Expand Up @@ -1110,7 +1111,7 @@ private void VisitIndirectFieldDecl(IndirectFieldDecl indirectFieldDecl)
code.Write(arraySize);
code.Write(')');
}
else if (!_config.GenerateLatestCode || arraySize == 1)
else if (_config.GenerateCompatibleCode || arraySize == 1)
{
code.Write(".AsSpan(");

Expand Down Expand Up @@ -1657,9 +1658,23 @@ private void VisitRecordDecl(RecordDecl recordDecl)
_outputBuilder.BeginValue(in valueDesc);

var code = _outputBuilder.BeginCSharpCode();
code.Write("(Guid*)Unsafe.AsPointer(ref Unsafe.AsRef(in ");

code.Write("(Guid*)Unsafe.AsPointer(");

if (!_config.GenerateLatestCode)
{
code.Write("ref Unsafe.AsRef(");
}

code.Write("in ");
code.Write(usableUuidName);
code.Write("))");
code.Write(')');

if (!_config.GenerateLatestCode)
{
code.Write(')');
}

_outputBuilder.EndCSharpCode(code);

_outputBuilder.EndValue(in valueDesc);
Expand Down Expand Up @@ -2213,6 +2228,7 @@ void OutputVtblHelperMethod(CXXRecordDecl cxxRecordDecl, CXXMethodDecl cxxMethod
HasFnPtrCodeGen = !_config.ExcludeFnptrCodegen,
IsCtxCxxRecord = true,
IsCxxRecordCtxUnsafe = IsUnsafe(cxxRecordDecl),
IsReadOnly = cxxMethodDecl.IsConst,
IsUnsafe = true,
NeedsReturnFixup = needsReturnFixup,
ReturnType = returnTypeName,
Expand Down Expand Up @@ -2335,7 +2351,27 @@ void OutputVtblHelperMethod(CXXRecordDecl cxxRecordDecl, CXXMethodDecl cxxMethod
{
body.Write('(');
body.Write(escapedCXXRecordDeclName);
body.Write("*)Unsafe.AsPointer(ref this)");
body.Write("*)Unsafe.AsPointer(");

if (cxxMethodDecl.IsConst)
{
if (!_config.GenerateLatestCode)
{
body.Write("ref Unsafe.AsRef(");
}

body.Write("in this");

if (!_config.GenerateLatestCode)
{
body.Write(')');
}
}
else
{
body.Write("ref this");
}
body.Write(')');
}
body.EndMarker("param");

Expand Down Expand Up @@ -2974,7 +3010,7 @@ void VisitConstantOrIncompleteArrayFieldDecl(RecordDecl recordDecl, FieldDecl co
AddDiagnostic(DiagnosticLevel.Info, $"{escapedName} (constant array field) has a size of 0", constantOrIncompleteArray);
}

if (!_config.GenerateLatestCode || (totalSize <= 1) || isUnsafeElementType)
if (_config.GenerateCompatibleCode || (totalSize <= 1) || isUnsafeElementType)
{
totalSizeString = null;
}
Expand Down Expand Up @@ -3100,7 +3136,7 @@ void VisitConstantOrIncompleteArrayFieldDecl(RecordDecl recordDecl, FieldDecl co
}
else if (totalSizeString is null)
{
_outputBuilder.BeginIndexer(AccessSpecifier.Public, isUnsafe: false, needsUnscopedRef: _config.GenerateLatestCode);
_outputBuilder.BeginIndexer(AccessSpecifier.Public, isUnsafe: false, needsUnscopedRef: !_config.GenerateCompatibleCode);
_outputBuilder.WriteIndexer($"ref {arrayTypeName}");
_outputBuilder.BeginIndexerParameters();
var param = new ParameterDesc {
Expand Down Expand Up @@ -3146,7 +3182,7 @@ void VisitConstantOrIncompleteArrayFieldDecl(RecordDecl recordDecl, FieldDecl co
ReturnType = $"Span<{arrayTypeName}>",
Location = constantOrIncompleteArray.Location,
HasBody = true,
NeedsUnscopedRef = _config.GenerateLatestCode,
NeedsUnscopedRef = !_config.GenerateCompatibleCode,
};

var isUnsafe = false;
Expand Down Expand Up @@ -3463,7 +3499,7 @@ private void VisitVarDecl(VarDecl varDecl)

case CX_SLK_UTF32:
{
typeName = (_config.GenerateLatestCode && flags.HasFlag(ValueFlags.Constant)) ? "ReadOnlySpan<uint>" : "uint[]";
typeName = (!_config.GenerateCompatibleCode && flags.HasFlag(ValueFlags.Constant)) ? "ReadOnlySpan<uint>" : "uint[]";
break;
}

Expand Down
82 changes: 71 additions & 11 deletions sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitStmt.cs
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ private void VisitCallExpr(CallExpr callExpr)
{
var args = callExpr.Args;

if (Config.GenerateLatestCode)
if (!Config.GenerateCompatibleCode)
{
outputBuilder.AddUsingDirective("System.Runtime.InteropServices");
outputBuilder.Write("NativeMemory.Copy");
Expand Down Expand Up @@ -306,13 +306,13 @@ private void VisitCallExpr(CallExpr callExpr)
break;
}

if (Config.GenerateLatestCode)
if (!Config.GenerateCompatibleCode)
{
args = [args[0], args[2], args[1]];
}
}

if (Config.GenerateLatestCode)
if (!Config.GenerateCompatibleCode)
{
outputBuilder.AddUsingDirective("System.Runtime.InteropServices");
outputBuilder.Write("NativeMemory.Fill");
Expand Down Expand Up @@ -409,7 +409,27 @@ void VisitArgs(CallExpr callExpr, IReadOnlyList<Expr>? args = null)
outputBuilder.AddUsingDirective("System.Runtime.CompilerServices");
outputBuilder.Write('(');
outputBuilder.Write(referenceTypeName);
outputBuilder.Write(")Unsafe.AsPointer(ref this)");
outputBuilder.Write(")Unsafe.AsPointer(");

if (referenceType.IsLocalConstQualified)
{
if (!_config.GenerateLatestCode)
{
outputBuilder.Write("ref Unsafe.AsRef(");
}

outputBuilder.Write("in this");

if (!_config.GenerateLatestCode)
{
outputBuilder.Write(')');
}
}
else
{
outputBuilder.Write("ref this");
}
outputBuilder.Write(')');

needsComma = true;
continue;
Expand All @@ -432,7 +452,27 @@ void VisitArgs(CallExpr callExpr, IReadOnlyList<Expr>? args = null)
else if (IsStmtAsWritten<CXXThisExpr>(arg, out _) && (functionName == "memcpy"))
{
outputBuilder.AddUsingDirective("System.Runtime.CompilerServices");
outputBuilder.Write("Unsafe.AsPointer(ref this)");
outputBuilder.Write("Unsafe.AsPointer(");

if (functionProtoType.ParamTypes[i].IsLocalConstQualified)
{
if (!_config.GenerateLatestCode)
{
outputBuilder.Write("ref Unsafe.AsRef(");
}

outputBuilder.Write("in this");

if (!_config.GenerateLatestCode)
{
outputBuilder.Write(')');
}
}
else
{
outputBuilder.Write("ref this");
}
outputBuilder.Write(')');

needsComma = true;
continue;
Expand Down Expand Up @@ -1756,7 +1796,7 @@ void HandleUnmanagedConstant(CSharpOutputBuilder outputBuilder, InitListExpr ini

outputBuilder.WriteIndented("ReadOnlySpan<byte> data = ");

if (_config.GenerateLatestCode)
if (!_config.GenerateCompatibleCode)
{
outputBuilder.WriteLine("[");
}
Expand All @@ -1772,7 +1812,7 @@ void HandleUnmanagedConstant(CSharpOutputBuilder outputBuilder, InitListExpr ini
outputBuilder.WriteNewline();
outputBuilder.DecreaseIndentation();

if (_config.GenerateLatestCode)
if (!_config.GenerateCompatibleCode)
{
outputBuilder.WriteIndented(']');
}
Expand Down Expand Up @@ -2190,7 +2230,27 @@ private void VisitReturnStmt(ReturnStmt returnStmt)
outputBuilder.AddUsingDirective("System.Runtime.CompilerServices");
outputBuilder.Write('(');
outputBuilder.Write(referenceTypeName);
outputBuilder.Write(")Unsafe.AsPointer(ref this)");
outputBuilder.Write(")Unsafe.AsPointer(");

if (referenceType.IsLocalConstQualified)
{
if (!_config.GenerateLatestCode)
{
outputBuilder.Write("ref Unsafe.AsRef(");
}

outputBuilder.Write("in this");

if (!_config.GenerateLatestCode)
{
outputBuilder.Write(')');
}
}
else
{
outputBuilder.Write("ref this");
}
outputBuilder.Write(')');

StopCSharpCode();
return;
Expand Down Expand Up @@ -2793,7 +2853,7 @@ private void VisitStringLiteral(StringLiteral stringLiteral)
case CX_SLK_Ordinary:
case CX_SLK_UTF8:
{
if (Config.GenerateLatestCode)
if (!Config.GenerateCompatibleCode)
{
outputBuilder.Write('"');
outputBuilder.Write(EscapeString(stringLiteral.String));
Expand Down Expand Up @@ -2851,7 +2911,7 @@ private void VisitStringLiteral(StringLiteral stringLiteral)

case CX_SLK_UTF32:
{
if (_config.GenerateLatestCode)
if (!_config.GenerateCompatibleCode)
{
outputBuilder.Write('[');
}
Expand All @@ -2872,7 +2932,7 @@ private void VisitStringLiteral(StringLiteral stringLiteral)

outputBuilder.Write("0x00000000");

if (_config.GenerateLatestCode)
if (!_config.GenerateCompatibleCode)
{
outputBuilder.Write(']');
}
Expand Down
4 changes: 2 additions & 2 deletions sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3274,7 +3274,7 @@ private static int GetAnonymousRecordIndex(RecordDecl recordDecl, RecordDecl par
index++;
}
}
else if ((parentIndex != 0) && (index > parentIndex))
else if ((parentIndex > 0) && (index > parentIndex))
{
if (recordDecl.IsUnion == parentRecordDecl.AnonymousRecords[parentIndex].IsUnion)
{
Expand Down Expand Up @@ -5588,7 +5588,7 @@ internal bool IsSupportedFixedSizedBufferType(string typeName)
case "ulong":
{
// We want to prefer InlineArray in modern code, as it is safer and supports more features
return !Config.GenerateLatestCode;
return Config.GenerateCompatibleCode;
}

default:
Expand Down
Loading
Loading