From a56a906efc6d2f69213e7e400063effb7db7f803 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Sun, 31 Aug 2025 17:56:21 -0700 Subject: [PATCH 1/5] Fix indentation of the CompareTo helper method --- sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs index 147788d7..dc722566 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs @@ -1412,7 +1412,7 @@ static void GenerateTransparentStructs(PInvokeGenerator generator, Stream? strea sw.Write(indentString); sw.WriteLine(" {"); sw.Write(indentString); - sw.Write(" if (obj is "); + sw.Write(" if (obj is "); sw.Write(name); sw.WriteLine(" other)"); sw.Write(indentString); From c83b59abb310e99f353d2e43b786722b2763448c Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Sun, 31 Aug 2025 18:41:30 -0700 Subject: [PATCH 2/5] Allow the command line to specify extra instance methods that should be readonly --- .../PInvokeGenerator.VisitDecl.cs | 8 +++----- .../PInvokeGenerator.cs | 9 +++++++++ .../PInvokeGeneratorConfiguration.cs | 16 ++++++++++++++++ sources/ClangSharpPInvokeGenerator/Program.cs | 16 ++++++++++++++++ .../Properties/launchSettings.json | 2 +- 5 files changed, 45 insertions(+), 6 deletions(-) diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs index 271e6583..fcfa5c6f 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs @@ -7,11 +7,9 @@ using System.Linq; using System.Runtime.InteropServices; using System.Text; -using System.Text.RegularExpressions; using ClangSharp.Abstractions; using ClangSharp.CSharp; using static ClangSharp.Interop.CX_CastKind; -using static ClangSharp.Interop.CX_CharacterKind; using static ClangSharp.Interop.CX_DeclKind; using static ClangSharp.Interop.CX_StmtClass; using static ClangSharp.Interop.CX_StorageClass; @@ -607,7 +605,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, + IsReadOnly = IsReadonly(cxxMethodDecl), IsUnsafe = IsUnsafe(functionDecl), IsCtxCxxRecord = cxxRecordDecl is not null, IsCxxRecordCtxUnsafe = cxxRecordDecl is not null && IsUnsafe(cxxRecordDecl), @@ -2228,7 +2226,7 @@ void OutputVtblHelperMethod(CXXRecordDecl cxxRecordDecl, CXXMethodDecl cxxMethod HasFnPtrCodeGen = !_config.ExcludeFnptrCodegen, IsCtxCxxRecord = true, IsCxxRecordCtxUnsafe = IsUnsafe(cxxRecordDecl), - IsReadOnly = cxxMethodDecl.IsConst, + IsReadOnly = IsReadonly(cxxMethodDecl), IsUnsafe = true, NeedsReturnFixup = needsReturnFixup, ReturnType = returnTypeName, @@ -2353,7 +2351,7 @@ void OutputVtblHelperMethod(CXXRecordDecl cxxRecordDecl, CXXMethodDecl cxxMethod body.Write(escapedCXXRecordDeclName); body.Write("*)Unsafe.AsPointer("); - if (cxxMethodDecl.IsConst) + if (IsReadonly(cxxMethodDecl)) { if (!_config.GenerateLatestCode) { diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs index dc722566..4461f887 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs @@ -5402,6 +5402,15 @@ private bool IsPrevContextStmt([MaybeNullWhen(false)] out T cursor, out objec } } + private bool IsReadonly(CXXMethodDecl? cxxMethodDecl) + { + if (cxxMethodDecl is not null) + { + return cxxMethodDecl.IsConst || HasRemapping(cxxMethodDecl, _config._withReadonlys, matchStar: true); + } + return false; + } + private static bool IsStmtAsWritten(Cursor cursor, [MaybeNullWhen(false)] out T value, bool removeParens = false) where T : Stmt { diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGeneratorConfiguration.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGeneratorConfiguration.cs index be02ba35..426f85a2 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGeneratorConfiguration.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGeneratorConfiguration.cs @@ -33,6 +33,7 @@ public sealed class PInvokeGeneratorConfiguration private readonly HashSet _nativeTypeNamesToStrip; private readonly HashSet _withManualImports; private readonly HashSet _traversalNames; + internal readonly HashSet _withReadonlys; internal readonly HashSet _withSetLastErrors; internal readonly HashSet _withSuppressGCTransitions; @@ -87,6 +88,7 @@ public PInvokeGeneratorConfiguration(string language, string languageStandard, s _nativeTypeNamesToStrip = new HashSet(StringComparer.Ordinal); _withManualImports = new HashSet(StringComparer.Ordinal); _traversalNames = new HashSet(StringComparer.Ordinal); + _withReadonlys = new HashSet(QualifiedNameComparer.Default); _withSetLastErrors = new HashSet(QualifiedNameComparer.Default); _withSuppressGCTransitions = new HashSet(QualifiedNameComparer.Default); @@ -516,6 +518,20 @@ public IReadOnlyDictionary WithNamespaces } } + [AllowNull] + public IReadOnlyCollection WithReadonlys + { + get + { + return _withReadonlys; + } + + init + { + AddRange(_withReadonlys, value); + } + } + [AllowNull] public IReadOnlyCollection WithSetLastErrors { diff --git a/sources/ClangSharpPInvokeGenerator/Program.cs b/sources/ClangSharpPInvokeGenerator/Program.cs index 04af033e..742c0290 100644 --- a/sources/ClangSharpPInvokeGenerator/Program.cs +++ b/sources/ClangSharpPInvokeGenerator/Program.cs @@ -54,6 +54,7 @@ internal static class Program private static readonly string[] s_withManualImportOptionAliases = ["--with-manual-import", "-wmi"]; private static readonly string[] s_withNamespaceOptionAliases = ["--with-namespace", "-wn"]; private static readonly string[] s_withPackingOptionAliases = ["--with-packing", "-wp"]; + private static readonly string[] s_withReadonlyOptionAliases = ["--with-readonly", "-wro"]; private static readonly string[] s_withSetLastErrorOptionAliases = ["--with-setlasterror", "-wsle"]; private static readonly string[] s_withSuppressGCTransitionOptionAliases = ["--with-suppressgctransition", "-wsgct"]; private static readonly string[] s_withTransparentStructOptionAliases = ["--with-transparent-struct", "-wts"]; @@ -92,6 +93,7 @@ internal static class Program private static readonly Option s_withManualImports = GetWithManualImportOption(); private static readonly Option s_withNamespaceNameValuePairs = GetWithNamespaceOption(); private static readonly Option s_withPackingNameValuePairs = GetWithPackingOption(); + private static readonly Option s_withReadonlys = GetWithReadonlyOption(); private static readonly Option s_withSetLastErrors = GetWithSetLastErrorOption(); private static readonly Option s_withSuppressGCTransitions = GetWithSuppressGCTransitionOption(); private static readonly Option s_withTransparentStructNameValuePairs = GetWithTransparentStructOption(); @@ -255,6 +257,7 @@ public static void Run(InvocationContext context) var withLibraryPathNameValuePairs = context.ParseResult.GetValueForOption(s_withLibraryPathNameValuePairs) ?? []; var withManualImports = context.ParseResult.GetValueForOption(s_withManualImports) ?? []; var withNamespaceNameValuePairs = context.ParseResult.GetValueForOption(s_withNamespaceNameValuePairs) ?? []; + var withReadonlys = context.ParseResult.GetValueForOption(s_withReadonlys) ?? []; var withSetLastErrors = context.ParseResult.GetValueForOption(s_withSetLastErrors) ?? []; var withSuppressGCTransitions = context.ParseResult.GetValueForOption(s_withSuppressGCTransitions) ?? []; var withTransparentStructNameValuePairs = context.ParseResult.GetValueForOption(s_withTransparentStructNameValuePairs) ?? []; @@ -729,6 +732,7 @@ public static void Run(InvocationContext context) WithLibraryPaths = withLibraryPaths, WithManualImports = withManualImports, WithNamespaces = withNamespaces, + WithReadonlys = withReadonlys, WithSetLastErrors = withSetLastErrors, WithSuppressGCTransitions = withSuppressGCTransitions, WithTransparentStructs = withTransparentStructs, @@ -1202,6 +1206,7 @@ private static RootCommand GetRootCommand() s_withManualImports, s_withNamespaceNameValuePairs, s_withPackingNameValuePairs, + s_withReadonlys, s_withSetLastErrors, s_withSuppressGCTransitions, s_withTransparentStructNameValuePairs, @@ -1350,6 +1355,17 @@ private static Option GetWithNamespaceOption() }; } + private static Option GetWithReadonlyOption() + { + return new Option( + aliases: s_withReadonlyOptionAliases, + description: "Add the readonly modifier to a given instance method. Supports wildcards.", + getDefaultValue: Array.Empty + ) { + AllowMultipleArgumentsPerToken = true + }; + } + private static Option GetWithSetLastErrorOption() { return new Option( diff --git a/sources/ClangSharpPInvokeGenerator/Properties/launchSettings.json b/sources/ClangSharpPInvokeGenerator/Properties/launchSettings.json index cef15055..fffd7084 100644 --- a/sources/ClangSharpPInvokeGenerator/Properties/launchSettings.json +++ b/sources/ClangSharpPInvokeGenerator/Properties/launchSettings.json @@ -12,7 +12,7 @@ "GenerateLocal": { "commandName": "Project", "commandLineArgs": "@generate.rsp", - "workingDirectory": "D:\\repos\\terrafx.interop.windows\\generation\\DirectX\\um\\dcommon" + "workingDirectory": "D:\\repos\\terrafx.interop.windows\\generation\\windows\\um\\wingdi" } } } From 0e10060a7643aafc52eb1d8a59909c2ab9dfcd13 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Sun, 31 Aug 2025 18:47:20 -0700 Subject: [PATCH 3/5] Ensure we compute the right anonymous record index --- .../ClangSharp.PInvokeGenerator/PInvokeGenerator.cs | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs index 4461f887..01eb5810 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs @@ -3258,29 +3258,22 @@ private static int GetAnonymousRecordIndex(RecordDecl recordDecl, RecordDecl par index++; } - if (parentRecordDecl.Parent is RecordDecl grandParentRecordDecl) + if (parentRecordDecl.Parent is RecordDecl grandparentRecordDecl) { - var parentIndex = GetAnonymousRecordIndex(parentRecordDecl, grandParentRecordDecl); + var parentIndex = GetAnonymousRecordIndex(parentRecordDecl, grandparentRecordDecl); // We can't have the nested anonymous record have the same name as the parent // so skip that index and just go one higher instead. This could still conflict // with another anonymous record at a different level, but that is less likely // and will still be unambiguous in total. - if (parentIndex == index) + if ((parentIndex == index) || ((parentIndex > 0) && (index > parentIndex))) { if (recordDecl.IsUnion == parentRecordDecl.IsUnion) { index++; } } - else if ((parentIndex > 0) && (index > parentIndex)) - { - if (recordDecl.IsUnion == parentRecordDecl.AnonymousRecords[parentIndex].IsUnion) - { - index++; - } - } } } } From ee8c6de9011a189ed1c209cccc8042f3da95afd6 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Sun, 31 Aug 2025 19:05:18 -0700 Subject: [PATCH 4/5] Ensure that labels are properly indented --- .../CSharp/CSharpOutputBuilder.cs | 25 +++++++++++++++++++ .../PInvokeGenerator.VisitStmt.cs | 4 +-- .../Properties/launchSettings.json | 2 +- 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/sources/ClangSharp.PInvokeGenerator/CSharp/CSharpOutputBuilder.cs b/sources/ClangSharp.PInvokeGenerator/CSharp/CSharpOutputBuilder.cs index 7c6c232d..f6d52daa 100644 --- a/sources/ClangSharp.PInvokeGenerator/CSharp/CSharpOutputBuilder.cs +++ b/sources/ClangSharp.PInvokeGenerator/CSharp/CSharpOutputBuilder.cs @@ -104,6 +104,31 @@ public void WriteIndentedLine(T value) WriteLine(value); } + public void WriteLabel(string name) + { + if (_currentLine.Length >= _indentationString.Length) + { + var match = true; + + for (var i = 0; i < _indentationString.Length; i++) + { + if (_currentLine[_currentLine.Length - i - 1] != _indentationString[_indentationString.Length - 1 - i]) + { + match = false; + break; + } + } + + if (match) + { + _ = _currentLine.Remove(_currentLine.Length - _indentationString.Length, _indentationString.Length); + } + } + + Write(name); + WriteLine(':'); + } + public void WriteLine(T value) { Write(value); diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitStmt.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitStmt.cs index 25ed853c..5eaf6372 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitStmt.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitStmt.cs @@ -2070,9 +2070,7 @@ private void VisitIntegerLiteral(IntegerLiteral integerLiteral) private void VisitLabelStmt(LabelStmt labelStmt) { var outputBuilder = StartCSharpCode(); - - outputBuilder.Write(labelStmt.Decl.Name); - outputBuilder.WriteLine(':'); + outputBuilder.WriteLabel(labelStmt.Decl.Name); outputBuilder.WriteIndentation(); Visit(labelStmt.SubStmt); diff --git a/sources/ClangSharpPInvokeGenerator/Properties/launchSettings.json b/sources/ClangSharpPInvokeGenerator/Properties/launchSettings.json index fffd7084..b961eec5 100644 --- a/sources/ClangSharpPInvokeGenerator/Properties/launchSettings.json +++ b/sources/ClangSharpPInvokeGenerator/Properties/launchSettings.json @@ -12,7 +12,7 @@ "GenerateLocal": { "commandName": "Project", "commandLineArgs": "@generate.rsp", - "workingDirectory": "D:\\repos\\terrafx.interop.windows\\generation\\windows\\um\\wingdi" + "workingDirectory": "D:\\repos\\terrafx.interop.windows\\generation\\windows\\um\\winioctl" } } } From 9d58e1393f8debcad247b62331c637a79a136235 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Sun, 31 Aug 2025 19:07:50 -0700 Subject: [PATCH 5/5] Update version to 20.1.2.3 --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 66a4d5e4..af219597 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -47,7 +47,7 @@ 17.0.0 ClangSharp ClangSharp - 20.1.2.2 + 20.1.2.3 rc1 pr