diff --git a/Directory.Build.targets b/Directory.Build.targets index 4390bae9..b4c1f5b5 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -37,7 +37,7 @@ - + diff --git a/README.md b/README.md index 3526ae9a..ce808223 100644 --- a/README.md +++ b/README.md @@ -194,6 +194,7 @@ Options: generate-tests-xunit Basic tests validating size, blittability, and associated metadata should be generated for XUnit. generate-aggressive-inlining [MethodImpl(MethodImplOptions.AggressiveInlining)] should be added to generated helper functions. generate-cpp-attributes [CppAttributeList("")] should be generated to document the encountered C++ attributes. + generate-doc-includes <include> xml documentation tags should be generated for declarations. generate-file-scoped-namespaces Namespaces should be scoped to the file to reduce nesting. generate-helper-types Code files should be generated for various helper attributes and declared transparent structs. generate-macro-bindings Bindings for macro-definitions should be generated. This currently only works with value like macros and not function-like ones. diff --git a/sources/ClangSharp.PInvokeGenerator/Abstractions/FieldDesc.cs b/sources/ClangSharp.PInvokeGenerator/Abstractions/FieldDesc.cs index 23e4a0da..ca13f2bc 100644 --- a/sources/ClangSharp.PInvokeGenerator/Abstractions/FieldDesc.cs +++ b/sources/ClangSharp.PInvokeGenerator/Abstractions/FieldDesc.cs @@ -10,6 +10,7 @@ internal struct FieldDesc public AccessSpecifier AccessSpecifier { get; set; } public string NativeTypeName { get; set; } public string EscapedName { get; set; } + public string ParentName { get; set; } public int? Offset { get; set; } public bool NeedsNewKeyword { get; set; } public bool HasBody { get; set; } diff --git a/sources/ClangSharp.PInvokeGenerator/Abstractions/FunctionOrDelegateDesc.cs b/sources/ClangSharp.PInvokeGenerator/Abstractions/FunctionOrDelegateDesc.cs index 79ec2b0d..4b8837ff 100644 --- a/sources/ClangSharp.PInvokeGenerator/Abstractions/FunctionOrDelegateDesc.cs +++ b/sources/ClangSharp.PInvokeGenerator/Abstractions/FunctionOrDelegateDesc.cs @@ -12,6 +12,7 @@ internal struct FunctionOrDelegateDesc public string NativeTypeName { get; set; } public string EscapedName { get; set; } public string EntryPoint { get; set; } + public string ParentName { get; set; } public string LibraryPath { get; set; } public string ReturnType { get; set; } public CallingConvention CallingConvention { get; set; } @@ -19,6 +20,7 @@ internal struct FunctionOrDelegateDesc public long? VtblIndex { get; set; } public CXSourceLocation? Location { get; set; } public bool HasBody { get; set; } + public bool IsInherited { get; set; } public bool IsVirtual { diff --git a/sources/ClangSharp.PInvokeGenerator/Abstractions/ValueDesc.cs b/sources/ClangSharp.PInvokeGenerator/Abstractions/ValueDesc.cs index a071d1c6..1c0aa1ae 100644 --- a/sources/ClangSharp.PInvokeGenerator/Abstractions/ValueDesc.cs +++ b/sources/ClangSharp.PInvokeGenerator/Abstractions/ValueDesc.cs @@ -11,6 +11,8 @@ internal struct ValueDesc public string TypeName { get; set; } public string EscapedName { get; set; } public string NativeTypeName { get; set; } + + public string ParentName { get; set; } public ValueKind Kind { get; set; } public ValueFlags Flags { get; set; } public CXSourceLocation? Location { get; set; } diff --git a/sources/ClangSharp.PInvokeGenerator/CSharp/CSharpOutputBuilder.VisitDecl.cs b/sources/ClangSharp.PInvokeGenerator/CSharp/CSharpOutputBuilder.VisitDecl.cs index bc558938..84d51f3c 100644 --- a/sources/ClangSharp.PInvokeGenerator/CSharp/CSharpOutputBuilder.VisitDecl.cs +++ b/sources/ClangSharp.PInvokeGenerator/CSharp/CSharpOutputBuilder.VisitDecl.cs @@ -31,6 +31,17 @@ public void EndUnchecked() public void BeginValue(in ValueDesc desc) { + if (_config.GenerateDocIncludes && (desc.Kind == ValueKind.Enumerator)) + { + WriteIndented("/// "); + } + if (desc.NativeTypeName is not null) { AddNativeTypeNameAttribute(desc.NativeTypeName); @@ -174,6 +185,11 @@ public void EndValue(in ValueDesc desc) case ValueKind.Enumerator: { WriteLine(','); + + if (_config.GenerateDocIncludes) + { + NeedsNewline = true; + } break; } @@ -204,6 +220,15 @@ public void EndValue(in ValueDesc desc) public void BeginEnum(in EnumDesc desc) { + if (_config.GenerateDocIncludes) + { + WriteIndented("/// "); + } + if (desc.NativeType is not null) { AddNativeTypeNameAttribute(desc.NativeType); @@ -234,6 +259,17 @@ public void BeginEnum(in EnumDesc desc) public void BeginField(in FieldDesc desc) { + if (_config.GenerateDocIncludes && !string.IsNullOrWhiteSpace(desc.ParentName)) + { + WriteIndented("/// "); + } + if (desc.Offset is not null) { WriteIndentedLine($"[FieldOffset({desc.Offset})]"); @@ -299,6 +335,28 @@ public void EndField(in FieldDesc desc) public void BeginFunctionOrDelegate(in FunctionOrDelegateDesc desc, ref bool isMethodClassUnsafe) { + if (_config.GenerateDocIncludes && !string.IsNullOrEmpty(desc.ParentName)) + { + if (desc.IsInherited) + { + WriteIndented("/// "); + } + else + { + WriteIndented("/// "); + } + } + if (desc.IsVirtual) { Debug.Assert(!desc.HasFnPtrCodeGen); @@ -599,6 +657,15 @@ public void EndFunctionOrDelegate(in FunctionOrDelegateDesc desc) public void BeginStruct(in StructDesc desc) { + if (_config.GenerateDocIncludes) + { + WriteIndented("/// "); + } + if (desc.LayoutAttribute is not null) { AddUsingDirective("System.Runtime.InteropServices"); @@ -687,7 +754,20 @@ public void BeginMarkerInterface(string[] baseTypeNames) public void BeginExplicitVtbl() { - WriteIndentedLine("public partial struct Vtbl"); + WriteIndented("public partial struct Vtbl"); + + if (_config.GenerateMarkerInterfaces && !_config.ExcludeFnptrCodegen) + { + WriteLine(""); + IncreaseIndentation(); + WriteIndentedLine("where TSelf : unmanaged, Interface"); + DecreaseIndentation(); + } + else + { + WriteNewline(); + } + WriteBlockStart(); } diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs index 0b434635..0e0ed742 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs @@ -263,11 +263,23 @@ private void VisitEnumConstantDecl(EnumConstantDecl enumConstantDecl) var escapedName = EscapeName(name); var typeName = GetTargetTypeName(enumConstantDecl, out _); var isAnonymousEnum = false; + var parentName = ""; - if ((enumConstantDecl.DeclContext is EnumDecl enumDecl) && GetRemappedCursorName(enumDecl).StartsWith("__AnonymousEnum_")) + if (enumConstantDecl.DeclContext is EnumDecl enumDecl) { - isAnonymousEnum = true; - accessSpecifier = GetAccessSpecifier(enumDecl); + parentName = GetRemappedCursorName(enumDecl); + + if (parentName.StartsWith("__AnonymousEnum_")) + { + parentName = ""; + isAnonymousEnum = true; + accessSpecifier = GetAccessSpecifier(enumDecl); + } + } + + if (string.IsNullOrEmpty(parentName)) + { + parentName = _outputBuilder.Name; } var kind = isAnonymousEnum ? ValueKind.Primitive : ValueKind.Enumerator; @@ -283,17 +295,17 @@ private void VisitEnumConstantDecl(EnumConstantDecl enumConstantDecl) TypeName = typeName, EscapedName = escapedName, NativeTypeName = null, + ParentName = parentName, Kind = kind, Flags = flags, Location = enumConstantDecl.Location, WriteCustomAttrs = static context => { - (var name, var generator) = ((string, PInvokeGenerator))context; - generator.WithAttributes(name); + (var enumConstantDecl, var generator) = ((EnumConstantDecl, PInvokeGenerator))context; - generator.WithUsings("*"); - generator.WithUsings(name); + generator.WithAttributes(enumConstantDecl); + generator.WithUsings(enumConstantDecl); }, - CustomAttrGeneratorData = (name, this), + CustomAttrGeneratorData = (enumConstantDecl, this), }; _outputBuilder.BeginValue(in desc); @@ -354,13 +366,12 @@ private void VisitEnumDecl(EnumDecl enumDecl) Location = enumDecl.Location, IsNested = enumDecl.DeclContext is TagDecl, WriteCustomAttrs = static context => { - (var name, var generator) = ((string, PInvokeGenerator))context; - generator.WithAttributes(name); + (var enumDecl, var generator) = ((EnumDecl, PInvokeGenerator))context; - generator.WithUsings("*"); - generator.WithUsings(name); + generator.WithAttributes(enumDecl); + generator.WithUsings(enumDecl); }, - CustomAttrGeneratorData = (name, this), + CustomAttrGeneratorData = (enumDecl, this), }; _outputBuilder.BeginEnum(in desc); @@ -401,17 +412,17 @@ private void VisitFieldDecl(FieldDecl fieldDecl) AccessSpecifier = accessSpecifier, NativeTypeName = nativeTypeName, EscapedName = escapedName, + ParentName = GetRemappedCursorName(fieldDecl.Parent), Offset = offset, NeedsNewKeyword = NeedsNewKeyword(name), Location = fieldDecl.Location, WriteCustomAttrs = static context => { - (var name, var generator) = ((string, PInvokeGenerator))context; - generator.WithAttributes(name); + (var fieldDecl, var generator) = ((FieldDecl, PInvokeGenerator))context; - generator.WithUsings("*"); - generator.WithUsings(name); + generator.WithAttributes(fieldDecl); + generator.WithUsings(fieldDecl); }, - CustomAttrGeneratorData = (name, this), + CustomAttrGeneratorData = (fieldDecl, this), }; _outputBuilder.BeginField(in desc); @@ -452,11 +463,13 @@ private void VisitFunctionDecl(FunctionDecl functionDecl) var name = GetRemappedCursorName(functionDecl); var className = name; + var parentName = ""; if (functionDecl.DeclContext is not CXXRecordDecl cxxRecordDecl) { cxxRecordDecl = null; className = GetClass(name); + parentName = className; StartUsingOutputBuilder(className); } else if ((Cursor)functionDecl.LexicalDeclContext != cxxRecordDecl) @@ -500,13 +513,14 @@ private void VisitFunctionDecl(FunctionDecl functionDecl) AccessSpecifier = accessSppecifier, NativeTypeName = nativeTypeName, EscapedName = escapedName, + ParentName = parentName, EntryPoint = entryPoint, CallingConvention = callingConventionName, LibraryPath = isDllImport ? GetLibraryPath(name).Unquote() : null, IsVirtual = isVirtual, IsDllImport = isDllImport, HasFnPtrCodeGen = !_config.ExcludeFnptrCodegen, - SetLastError = GetSetLastError(name), + SetLastError = GetSetLastError(functionDecl), IsCxx = cxxMethodDecl is not null, IsStatic = isDllImport || (cxxMethodDecl?.IsStatic ?? true), NeedsNewKeyword = NeedsNewKeyword(escapedName, functionDecl.Parameters), @@ -519,13 +533,17 @@ private void VisitFunctionDecl(FunctionDecl functionDecl) Location = functionDecl.Location, HasBody = body is not null, WriteCustomAttrs = static context => { - (var name, var generator) = ((string, PInvokeGenerator))context; - generator.WithAttributes(name); + (var functionDecl, var outputBuilder, var generator) = ((FunctionDecl, IOutputBuilder, PInvokeGenerator))context; - generator.WithUsings("*"); - generator.WithUsings(name); + generator.WithAttributes(functionDecl); + generator.WithUsings(functionDecl); + + if (generator.HasSuppressGCTransition(functionDecl)) + { + outputBuilder.WriteCustomAttribute("SuppressGCTransition"); + } }, - CustomAttrGeneratorData = (name, this), + CustomAttrGeneratorData = (functionDecl, _outputBuilder, this), }; _ = _isTopLevelClassUnsafe.TryGetValue(className, out var isUnsafe); @@ -548,14 +566,6 @@ private void VisitFunctionDecl(FunctionDecl functionDecl) var parameterDesc = new ParameterDesc { Name = "pThis", Type = $"{cxxRecordEscapedName}*", - WriteCustomAttrs = static context => { - (var name, var generator) = ((string, PInvokeGenerator))context; - generator.WithAttributes(name); - - generator.WithUsings("*"); - generator.WithUsings(name); - }, - CustomAttrGeneratorData = ("pThis", this), }; _outputBuilder.BeginParameter(in parameterDesc); @@ -748,18 +758,18 @@ private void VisitIndirectFieldDecl(IndirectFieldDecl indirectFieldDecl) AccessSpecifier = accessSpecifier, NativeTypeName = null, EscapedName = escapedName, + ParentName = GetRemappedCursorName(fieldDecl.Parent), Offset = null, NeedsNewKeyword = false, Location = fieldDecl.Location, HasBody = true, WriteCustomAttrs = static context => { - (var name, var generator) = ((string, PInvokeGenerator))context; - generator.WithAttributes(name); + (var fieldDecl, var generator) = ((FieldDecl, PInvokeGenerator))context; - generator.WithUsings("*"); - generator.WithUsings(name); + generator.WithAttributes(fieldDecl); + generator.WithUsings(fieldDecl); }, - CustomAttrGeneratorData = (name, this), + CustomAttrGeneratorData = (fieldDecl, this), }; _outputBuilder.WriteDivider(true); @@ -1002,11 +1012,10 @@ void ForFunctionDecl(ParmVarDecl parmVarDecl, FunctionDecl functionDecl) : null, Location = parmVarDecl.Location, WriteCustomAttrs = static context => { - (var name, var generator, var csharpOutputBuilder, var defaultArg) = ((string, PInvokeGenerator, CSharp.CSharpOutputBuilder, Expr))context; - generator.WithAttributes(name); + (var parmVarDecl, var generator, var csharpOutputBuilder, var defaultArg) = ((ParmVarDecl, PInvokeGenerator, CSharp.CSharpOutputBuilder, Expr))context; - generator.WithUsings("*"); - generator.WithUsings(name); + generator.WithAttributes(parmVarDecl); + generator.WithUsings(parmVarDecl); if (defaultArg is not null) { @@ -1020,7 +1029,7 @@ void ForFunctionDecl(ParmVarDecl parmVarDecl, FunctionDecl functionDecl) csharpOutputBuilder.WriteCustomAttribute("Optional", null); } }, - CustomAttrGeneratorData = (name, this, null as CSharp.CSharpOutputBuilder, null as Expr), + CustomAttrGeneratorData = (parmVarDecl, this, null as CSharp.CSharpOutputBuilder, null as Expr), }; var handledDefaultArg = false; @@ -1036,7 +1045,7 @@ void ForFunctionDecl(ParmVarDecl parmVarDecl, FunctionDecl functionDecl) return _config.WithTransparentStructs.ContainsKey(typeName); }))) { - desc.CustomAttrGeneratorData = (name, this, csharpOutputBuilder, isExprDefaultValue ? null : parmVarDecl.DefaultArg); + desc.CustomAttrGeneratorData = (parmVarDecl, this, csharpOutputBuilder, isExprDefaultValue ? null : parmVarDecl.DefaultArg); handledDefaultArg = true; } } @@ -1103,13 +1112,12 @@ void ForTypedefDecl(ParmVarDecl parmVarDecl, TypedefDecl typedefDecl) : null, Location = parmVarDecl.Location, WriteCustomAttrs = static context => { - (var name, var generator) = ((string, PInvokeGenerator))context; - generator.WithAttributes(name); + (var parmVarDecl, var generator) = ((ParmVarDecl, PInvokeGenerator))context; - generator.WithUsings("*"); - generator.WithUsings(name); + generator.WithAttributes(parmVarDecl); + generator.WithUsings(parmVarDecl); }, - CustomAttrGeneratorData = (name, this), + CustomAttrGeneratorData = (parmVarDecl, this), }; _outputBuilder.BeginParameter(in desc); @@ -1171,8 +1179,7 @@ private void VisitRecordDecl(RecordDecl recordDecl) _testOutputBuilder.Write(escapedName); _testOutputBuilder.WriteLine("\" /> struct."); - WithAttributes("*", onlySupportedOSPlatform: true, isTestOutput: true); - WithAttributes(name, onlySupportedOSPlatform: true, isTestOutput: true); + WithAttributes(recordDecl, onlySupportedOSPlatform: true, isTestOutput: true); _testOutputBuilder.WriteIndented("public static unsafe partial class "); _testOutputBuilder.Write(escapedName); @@ -1298,13 +1305,12 @@ private void VisitRecordDecl(RecordDecl recordDecl) Location = recordDecl.Location, IsNested = recordDecl.DeclContext is TagDecl, WriteCustomAttrs = static context => { - (var name, var generator) = ((string, PInvokeGenerator))context; - generator.WithAttributes(name); + (var recordDecl, var generator) = ((RecordDecl, PInvokeGenerator))context; - generator.WithUsings("*"); - generator.WithUsings(name); + generator.WithAttributes(recordDecl); + generator.WithUsings(recordDecl); }, - CustomAttrGeneratorData = (name, this), + CustomAttrGeneratorData = (recordDecl, this), }; if (!isTopLevelStruct) @@ -1378,14 +1384,6 @@ private void VisitRecordDecl(RecordDecl recordDecl) EscapedName = "lpVtbl", Offset = null, NeedsNewKeyword = false, - WriteCustomAttrs = static context => { - (var name, var generator) = ((string, PInvokeGenerator))context; - generator.WithAttributes(name); - - generator.WithUsings("*"); - generator.WithUsings(name); - }, - CustomAttrGeneratorData = ("lpVtbl", this), }; _outputBuilder.BeginField(in fieldDesc); @@ -1422,14 +1420,6 @@ private void VisitRecordDecl(RecordDecl recordDecl) NeedsNewKeyword = false, InheritedFrom = parent, Location = cxxBaseSpecifier.Location, - WriteCustomAttrs = static context => { - (var name, var generator) = ((string, PInvokeGenerator))context; - generator.WithAttributes(name); - - generator.WithUsings("*"); - generator.WithUsings(name); - }, - CustomAttrGeneratorData = (baseFieldName, this), }; _outputBuilder.BeginField(in fieldDesc); @@ -1789,13 +1779,12 @@ void OutputMarkerInterface(CXXRecordDecl cxxRecordDecl, CXXMethodDecl cxxMethodD VtblIndex = _config.GenerateVtblIndexAttribute ? cxxMethodDecl.VtblIndex : -1, Location = cxxMethodDecl.Location, WriteCustomAttrs = static context => { - (var name, var generator) = ((string, PInvokeGenerator))context; - generator.WithAttributes(name); + (var cxxMethodDecl, var generator) = ((CXXMethodDecl, PInvokeGenerator))context; - generator.WithUsings("*"); - generator.WithUsings(name); + generator.WithAttributes(cxxMethodDecl); + generator.WithUsings(cxxMethodDecl); }, - CustomAttrGeneratorData = (name, this), + CustomAttrGeneratorData = (cxxMethodDecl, this), }; var isUnsafe = true; @@ -1859,6 +1848,12 @@ void OutputVtblEntry(CXXRecordDecl cxxRecordDecl, CXXMethodDecl cxxMethodDecl) var cxxMethodDeclTypeName = GetRemappedTypeName(cxxMethodDecl, cxxRecordDecl, cxxMethodDecl.Type, out var nativeTypeName, skipUsing: false, ignoreTransparentStructsWhereRequired: true); + if (_config.GenerateMarkerInterfaces && !_config.ExcludeFnptrCodegen) + { + var cxxRecordDeclName = GetRemappedCursorName(cxxRecordDecl); + cxxMethodDeclTypeName = cxxMethodDeclTypeName.Replace($"<{cxxRecordDeclName}*,", " { - (var name, var generator) = ((string, PInvokeGenerator))context; - generator.WithAttributes(name); + (var cxxMethodDecl, var generator) = ((CXXMethodDecl, PInvokeGenerator))context; - generator.WithUsings("*"); - generator.WithUsings(name); + generator.WithAttributes(cxxMethodDecl); + generator.WithUsings(cxxMethodDecl); }, - CustomAttrGeneratorData = (remappedName, this), + CustomAttrGeneratorData = (cxxMethodDecl, this), }; _outputBuilder.BeginField(in desc); @@ -1908,6 +1902,15 @@ void OutputVtblHelperMethod(CXXRecordDecl cxxRecordDecl, CXXMethodDecl cxxMethod var name = GetRemappedCursorName(cxxMethodDecl); var needsReturnFixup = false; var needsCastToTransparentStruct = false; + var cxxRecordDeclName = GetRemappedCursorName(cxxRecordDecl); + var parentName = cxxRecordDeclName; + var isInherited = false; + + if (cxxMethodDecl.Parent != cxxRecordDecl) + { + parentName = GetRemappedCursorName(cxxMethodDecl.Parent); + isInherited = true; + } if (returnType.Kind != CXTypeKind.CXType_Void) { @@ -1919,7 +1922,9 @@ void OutputVtblHelperMethod(CXXRecordDecl cxxRecordDecl, CXXMethodDecl cxxMethod AccessSpecifier = AccessSpecifier.Public, IsAggressivelyInlined = _config.GenerateAggressiveInlining, EscapedName = EscapeAndStripName(name), + ParentName = parentName, IsMemberFunction = true, + IsInherited = isInherited, NativeTypeName = nativeTypeName, NeedsNewKeyword = NeedsNewKeyword(name, cxxMethodDecl.Parameters), HasFnPtrCodeGen = !_config.ExcludeFnptrCodegen, @@ -1932,13 +1937,12 @@ void OutputVtblHelperMethod(CXXRecordDecl cxxRecordDecl, CXXMethodDecl cxxMethod Location = cxxMethodDecl.Location, HasBody = true, WriteCustomAttrs = static context => { - (var name, var generator) = ((string, PInvokeGenerator))context; - generator.WithAttributes(name); + (var cxxMethodDecl, var generator) = ((CXXMethodDecl, PInvokeGenerator))context; - generator.WithUsings("*"); - generator.WithUsings(name); + generator.WithAttributes(cxxMethodDecl); + generator.WithUsings(cxxMethodDecl); }, - CustomAttrGeneratorData = (name, this), + CustomAttrGeneratorData = (cxxMethodDecl, this), }; var isUnsafe = true; @@ -1951,7 +1955,6 @@ void OutputVtblHelperMethod(CXXRecordDecl cxxRecordDecl, CXXMethodDecl cxxMethod _outputBuilder.EndFunctionInnerPrototype(); _outputBuilder.BeginBody(); - var cxxRecordDeclName = GetRemappedCursorName(cxxRecordDecl); var escapedCXXRecordDeclName = EscapeName(cxxRecordDeclName); _outputBuilder.BeginInnerFunctionBody(); @@ -2171,14 +2174,6 @@ void VisitBitfieldDecl(FieldDecl fieldDecl, Type[] types, RecordDecl recordDecl, Offset = fieldDecl.Parent.IsUnion ? 0 : null, NeedsNewKeyword = false, Location = fieldDecl.Location, - WriteCustomAttrs = static context => { - (var name, var generator) = ((string, PInvokeGenerator))context; - generator.WithAttributes(name); - - generator.WithUsings("*"); - generator.WithUsings(name); - }, - CustomAttrGeneratorData = (bitfieldName, this), }; _outputBuilder.BeginField(in fieldDesc); _outputBuilder.WriteRegularField(typeNameBacking, bitfieldName); @@ -2363,18 +2358,18 @@ void VisitBitfieldDecl(FieldDecl fieldDecl, Type[] types, RecordDecl recordDecl, AccessSpecifier = accessSpecifier, NativeTypeName = nativeTypeName, EscapedName = escapedName, + ParentName = GetRemappedCursorName(fieldDecl.Parent), Offset = null, NeedsNewKeyword = false, Location = fieldDecl.Location, HasBody = true, WriteCustomAttrs = static context => { - (var name, var generator) = ((string, PInvokeGenerator))context; - generator.WithAttributes(name); + (var fieldDecl, var generator) = ((FieldDecl, PInvokeGenerator))context; - generator.WithUsings("*"); - generator.WithUsings(name); + generator.WithAttributes(fieldDecl); + generator.WithUsings(fieldDecl); }, - CustomAttrGeneratorData = (name, this), + CustomAttrGeneratorData = (fieldDecl, this), }; _outputBuilder.WriteDivider(); @@ -2621,13 +2616,12 @@ void VisitConstantArrayFieldDecl(RecordDecl recordDecl, FieldDecl constantArray) Location = constantArray.Location, IsNested = true, WriteCustomAttrs = static context => { - (var name, var generator) = ((string, PInvokeGenerator))context; - generator.WithAttributes(name); + (var fieldDecl, var generator) = ((FieldDecl, PInvokeGenerator))context; - generator.WithUsings("*"); - generator.WithUsings(name); + generator.WithAttributes(fieldDecl); + generator.WithUsings(fieldDecl); }, - CustomAttrGeneratorData = (name, this), + CustomAttrGeneratorData = (constantArray, this), }; _outputBuilder.BeginStruct(in desc); @@ -2673,14 +2667,6 @@ void VisitConstantArrayFieldDecl(RecordDecl recordDecl, FieldDecl constantArray) Offset = null, NeedsNewKeyword = false, Location = constantArray.Location, - WriteCustomAttrs = static context => { - (var name, var generator) = ((string, PInvokeGenerator))context; - generator.WithAttributes(name); - - generator.WithUsings("*"); - generator.WithUsings(name); - }, - CustomAttrGeneratorData = (fieldName, this), }; _outputBuilder.BeginField(in fieldDesc); @@ -2702,14 +2688,6 @@ void VisitConstantArrayFieldDecl(RecordDecl recordDecl, FieldDecl constantArray) var param = new ParameterDesc { Name = "index", Type = "int", - WriteCustomAttrs = static context => { - (var name, var generator) = ((string, PInvokeGenerator))context; - generator.WithAttributes(name); - - generator.WithUsings("*"); - generator.WithUsings(name); - }, - CustomAttrGeneratorData = ("index", this), }; _outputBuilder.BeginParameter(in param); _outputBuilder.EndParameter(in param); @@ -2743,14 +2721,6 @@ void VisitConstantArrayFieldDecl(RecordDecl recordDecl, FieldDecl constantArray) var param = new ParameterDesc { Name = "index", Type = "int", - WriteCustomAttrs = static context => { - (var name, var generator) = ((string, PInvokeGenerator))context; - generator.WithAttributes(name); - - generator.WithUsings("*"); - generator.WithUsings(name); - }, - CustomAttrGeneratorData = ("index", this), }; _outputBuilder.BeginParameter(in param); _outputBuilder.EndParameter(in param); @@ -2787,14 +2757,6 @@ void VisitConstantArrayFieldDecl(RecordDecl recordDecl, FieldDecl constantArray) ReturnType = $"Span<{typeName}>", Location = constantArray.Location, HasBody = true, - WriteCustomAttrs = static context => { - (var name, var generator) = ((string, PInvokeGenerator))context; - generator.WithAttributes(name); - - generator.WithUsings("*"); - generator.WithUsings(name); - }, - CustomAttrGeneratorData = ("AsSpan", this), }; var isUnsafe = false; @@ -2807,14 +2769,6 @@ void VisitConstantArrayFieldDecl(RecordDecl recordDecl, FieldDecl constantArray) param = new ParameterDesc { Name = "length", Type = "int", - WriteCustomAttrs = static context => { - (var name, var generator) = ((string, PInvokeGenerator))context; - generator.WithAttributes(name); - - generator.WithUsings("*"); - generator.WithUsings(name); - }, - CustomAttrGeneratorData = ("length", this), }; _outputBuilder.BeginParameter(in param); @@ -2892,13 +2846,12 @@ void ForFunctionProtoType(TypedefDecl typedefDecl, FunctionProtoType functionPro Location = typedefDecl.Location, HasBody = true, WriteCustomAttrs = static context => { - (var name, var generator) = ((string, PInvokeGenerator))context; - generator.WithAttributes(name); + (var typedefDecl, var generator) = ((TypedefDecl, PInvokeGenerator))context; - generator.WithUsings("*"); - generator.WithUsings(name); + generator.WithAttributes(typedefDecl); + generator.WithUsings(typedefDecl); }, - CustomAttrGeneratorData = (name, this), + CustomAttrGeneratorData = (typedefDecl, this), }; var isUnsafe = desc.IsUnsafe; @@ -3211,13 +3164,12 @@ private void VisitVarDecl(VarDecl varDecl) Flags = flags, Location = varDecl.Location, WriteCustomAttrs = static context => { - (var name, var generator) = ((string, PInvokeGenerator))context; - generator.WithAttributes(name); + (var varDecl, var generator) = ((VarDecl, PInvokeGenerator))context; - generator.WithUsings("*"); - generator.WithUsings(name); + generator.WithAttributes(varDecl); + generator.WithUsings(varDecl); }, - CustomAttrGeneratorData = (name, this), + CustomAttrGeneratorData = (varDecl, this), }; if (openedOutputBuilder) diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs index 066e32d1..d6b3db5f 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs @@ -1914,9 +1914,7 @@ private CallingConvention GetCallingConvention(Cursor cursor, Cursor context, Ty { if (cursor is NamedDecl namedDecl) { - var remappedName = GetRemappedCursorName(namedDecl); - - if (_config.WithCallConvs.TryGetValue(remappedName, out var callConv) || _config.WithCallConvs.TryGetValue("*", out callConv)) + if (TryGetRemappedValue(namedDecl, _config.WithCallConvs, out var callConv, matchStar: true)) { if (Enum.TryParse(callConv, true, out var remappedCallingConvention)) { @@ -2579,8 +2577,7 @@ private string GetRemappedTypeName(Cursor cursor, Cursor context, Type type, out remappedName = "uint"; } - WithType("*", ref remappedName, ref nativeTypeName); - WithType(enumDeclName, ref remappedName, ref nativeTypeName); + WithType(enumDecl, ref remappedName, ref nativeTypeName); } } @@ -3072,13 +3069,23 @@ private string GetTypeNameForPointeeType(Cursor cursor, Cursor context, Type roo if (!isMacroDefinitionRecord) { _ = nameBuilder.Append(" unmanaged"); + var hasSuppressGCTransition = HasSuppressGCTransition(cursor); if (callConv != CallingConvention.Winapi) { _ = nameBuilder.Append('['); _ = nameBuilder.Append(callConv.AsString(true)); + + if (hasSuppressGCTransition) + { + _ = nameBuilder.Append(", SuppressGCTransition"); + } _ = nameBuilder.Append(']'); } + else if (hasSuppressGCTransition) + { + _ = nameBuilder.Append("[SuppressGCTransition]"); + } } _ = nameBuilder.Append('<'); @@ -3668,6 +3675,15 @@ private void GetTypeSize(Cursor cursor, Type type, ref long alignment32, ref lon } } + private bool HasSuppressGCTransition(Cursor cursor) + { + if (cursor is not NamedDecl namedDecl) + { + return false; + } + return HasRemapping(namedDecl, _config.WithSuppressGCTransitions); + } + private bool HasVtbl(CXXRecordDecl cxxRecordDecl) { var hasDirectVtbl = cxxRecordDecl.Methods.Any((method) => method.IsVirtual); @@ -3779,7 +3795,7 @@ bool IsExcludedByFile(Cursor cursor) declLocation.GetExpansionLocation(out var expansionFile, out var expansionLine, out var expansionColumn, out _); - if ((expansionFile == file) && (expansionLine == line) && (expansionColumn == column) && (_config.TraversalNames.Length != 0)) + if ((expansionFile == file) && (expansionLine == line) && (expansionColumn == column) && _config.TraversalNames.Any()) { // clang_getLocation is a very expensive call, so exit early if the expansion file is the same // However, if we are not explicitly specifying traversal names, its possible the expansion location @@ -3920,7 +3936,7 @@ bool IsExcludedByName(Cursor cursor, ref uint isExcludedValue) return true; } - if ((_config.IncludedNames.Length != 0) && !_config.IncludedNames.Contains(qualifiedName) && !_config.IncludedNames.Contains(dottedQualifiedName) && !_config.IncludedNames.Contains(name)) + if (_config.IncludedNames.Any() && !_config.IncludedNames.Contains(qualifiedName) && !_config.IncludedNames.Contains(dottedQualifiedName) && !_config.IncludedNames.Contains(name)) { if (_config.LogExclusions) { @@ -3958,7 +3974,7 @@ bool IsIncludedFileOrLocation(Cursor cursor, CXFile file, CXSourceLocation locat { return true; } - else if ((_config.TraversalNames.Length == 0) && location.IsFromMainFile) + else if (!_config.TraversalNames.Any() && location.IsFromMainFile) { return true; } @@ -5162,7 +5178,7 @@ private void StartUsingOutputBuilder(string name, bool includeTestOutput = false { if (!_outputBuilderFactory.TryGetOutputBuilder(nameTests, out var testOutputBuilder)) { - CreateTestOutputBuilder(nameTests); + CreateTestOutputBuilder(name, nameTests); } else { @@ -5182,7 +5198,7 @@ private void StartUsingOutputBuilder(string name, bool includeTestOutput = false if (includeTestOutput && !string.IsNullOrWhiteSpace(_config.TestOutputLocation)) { - CreateTestOutputBuilder(nameTests); + CreateTestOutputBuilder(name, nameTests); } } else @@ -5193,7 +5209,7 @@ private void StartUsingOutputBuilder(string name, bool includeTestOutput = false { if (!_outputBuilderFactory.TryGetOutputBuilder(nameTests, out var testOutputBuilder)) { - CreateTestOutputBuilder(nameTests); + CreateTestOutputBuilder(name, nameTests); } else { @@ -5206,9 +5222,9 @@ private void StartUsingOutputBuilder(string name, bool includeTestOutput = false } _outputBuilderUsers++; - void CreateTestOutputBuilder(string name) + void CreateTestOutputBuilder(string name, string nameTests) { - _testOutputBuilder = _outputBuilderFactory.CreateTests(name); + _testOutputBuilder = _outputBuilderFactory.CreateTests(nameTests); var isTopLevelStruct = _config.WithTypes.TryGetValue(name, out var withType) && (withType == "struct"); @@ -5525,11 +5541,11 @@ private void Visit(IEnumerable cursors) private void Visit(IEnumerable cursors, IEnumerable excludedCursors) => Visit(cursors.Except(excludedCursors)); - private void WithAttributes(string remappedName, bool onlySupportedOSPlatform = false, bool isTestOutput = false) + private void WithAttributes(NamedDecl namedDecl, bool onlySupportedOSPlatform = false, bool isTestOutput = false) { var outputBuilder = isTestOutput ? _testOutputBuilder : _outputBuilder; - if (_config.WithAttributes.TryGetValue(remappedName, out var attributes)) + if (TryGetRemappedValue(namedDecl, _config.WithAttributes, out var attributes)) { foreach (var attribute in attributes.Where((a) => !onlySupportedOSPlatform || a.StartsWith("SupportedOSPlatform("))) { @@ -5625,13 +5641,77 @@ private bool TryGetNamespace(string remappedName, out string namespaceName) return _config.WithNamespaces.TryGetValue(remappedName, out namespaceName); } - private bool GetSetLastError(string remappedName) => _config.WithSetLastErrors.Contains("*") || - _config.WithSetLastErrors.Contains(remappedName); + private bool GetSetLastError(NamedDecl namedDecl) => HasRemapping(namedDecl, _config.WithSetLastErrors, matchStar: true); + + private bool HasRemapping(NamedDecl namedDecl, IReadOnlyCollection entries, bool matchStar = false) + { + var name = GetCursorQualifiedName(namedDecl); + + if (name.StartsWith("ClangSharpMacro_")) + { + name = name["ClangSharpMacro_".Length..]; + } + + if (entries.Contains(name)) + { + return true; + } + + name = name.Replace("::", "."); + + if (entries.Contains(name)) + { + return true; + } + + name = GetCursorQualifiedName(namedDecl, truncateFunctionParameters: true); + + if (name.StartsWith("ClangSharpMacro_")) + { + name = name["ClangSharpMacro_".Length..]; + } + + if (entries.Contains(name)) + { + return true; + } + + name = name.Replace("::", "."); + + if (entries.Contains(name)) + { + return true; + } + + name = GetRemappedCursorName(namedDecl); + + if (name.StartsWith("ClangSharpMacro_")) + { + name = name["ClangSharpMacro_".Length..]; + } + + if (entries.Contains(name)) + { + return true; + } + + if (matchStar && entries.Contains("*")) + { + return true; + } + + return false; + } - private bool TryGetRemappedValue(NamedDecl namedDecl, IReadOnlyDictionary remappings, out T value) + private bool TryGetRemappedValue(NamedDecl namedDecl, IReadOnlyDictionary remappings, out T value, bool matchStar = false) { var name = GetCursorQualifiedName(namedDecl); + if (name.StartsWith("ClangSharpMacro_")) + { + name = name["ClangSharpMacro_".Length..]; + } + if (remappings.TryGetValue(name, out value)) { return true; @@ -5646,6 +5726,11 @@ private bool TryGetRemappedValue(NamedDecl namedDecl, IReadOnlyDictionary(NamedDecl namedDecl, IReadOnlyDictionary _forceRemappedNames; - private readonly Dictionary _remappedNames; - private readonly Dictionary _withAccessSpecifiers; - private readonly Dictionary> _withAttributes; - private readonly Dictionary _withCallConvs; - private readonly Dictionary _withClasses; - private readonly Dictionary _withLibraryPaths; - private readonly Dictionary _withNamespaces; - private readonly Dictionary _withTransparentStructs; - private readonly Dictionary _withTypes; - private readonly Dictionary> _withUsings; + private readonly SortedSet _excludedNames; + private readonly SortedSet _forceRemappedNames; + private readonly SortedSet _includedNames; + private readonly SortedSet _traversalNames; + private readonly SortedSet _withSetLastErrors; + private readonly SortedSet _withSuppressGCTransitions; + + private readonly SortedDictionary _remappedNames; + private readonly SortedDictionary _withAccessSpecifiers; + private readonly SortedDictionary> _withAttributes; + private readonly SortedDictionary _withCallConvs; + private readonly SortedDictionary _withClasses; + private readonly SortedDictionary _withLibraryPaths; + private readonly SortedDictionary _withNamespaces; + private readonly SortedDictionary _withTransparentStructs; + private readonly SortedDictionary _withTypes; + private readonly SortedDictionary> _withUsings; private PInvokeGeneratorConfigurationOptions _options; - public PInvokeGeneratorConfiguration(string libraryPath, string namespaceName, string outputLocation, string testOutputLocation, PInvokeGeneratorOutputMode outputMode = PInvokeGeneratorOutputMode.CSharp, PInvokeGeneratorConfigurationOptions options = PInvokeGeneratorConfigurationOptions.None, string[] excludedNames = null, string[] includedNames = null, string headerFile = null, string methodClassName = null, string methodPrefixToStrip = null, IReadOnlyDictionary remappedNames = null, string[] traversalNames = null, IReadOnlyDictionary withAccessSpecifiers = null, IReadOnlyDictionary> withAttributes = null, IReadOnlyDictionary withCallConvs = null, IReadOnlyDictionary withClasses = null, IReadOnlyDictionary withLibraryPaths = null, IReadOnlyDictionary withNamespaces = null, string[] withSetLastErrors = null, IReadOnlyDictionary withTransparentStructs = null, IReadOnlyDictionary withTypes = null, IReadOnlyDictionary> withUsings = null) + public PInvokeGeneratorConfiguration(string libraryPath, string namespaceName, string outputLocation, string testOutputLocation, PInvokeGeneratorOutputMode outputMode = PInvokeGeneratorOutputMode.CSharp, PInvokeGeneratorConfigurationOptions options = PInvokeGeneratorConfigurationOptions.None, IReadOnlyCollection excludedNames = null, IReadOnlyCollection includedNames = null, string headerFile = null, string methodClassName = null, string methodPrefixToStrip = null, IReadOnlyDictionary remappedNames = null, IReadOnlyCollection traversalNames = null, IReadOnlyDictionary withAccessSpecifiers = null, IReadOnlyDictionary> withAttributes = null, IReadOnlyDictionary withCallConvs = null, IReadOnlyDictionary withClasses = null, IReadOnlyDictionary withLibraryPaths = null, IReadOnlyDictionary withNamespaces = null, IReadOnlyCollection withSetLastErrors = null, IReadOnlyCollection withSuppressGCTransitions = null, IReadOnlyDictionary withTransparentStructs = null, IReadOnlyDictionary withTypes = null, IReadOnlyDictionary> withUsings = null) { - if (excludedNames is null) - { - excludedNames = Array.Empty(); - } - - if (includedNames is null) - { - includedNames = Array.Empty(); - } - if (string.IsNullOrWhiteSpace(libraryPath)) { libraryPath = string.Empty; @@ -83,32 +79,27 @@ public PInvokeGeneratorConfiguration(string libraryPath, string namespaceName, s throw new ArgumentNullException(nameof(outputLocation)); } - if (traversalNames is null) - { - traversalNames = Array.Empty(); - } + _options = options; - if (withSetLastErrors is null) - { - withSetLastErrors = Array.Empty(); - } + _excludedNames = new SortedSet(); + _forceRemappedNames = new SortedSet(); + _includedNames = new SortedSet(); + _traversalNames = new SortedSet(); + _withSetLastErrors = new SortedSet(); + _withSuppressGCTransitions = new SortedSet(); + + _remappedNames = new SortedDictionary(); + _withAccessSpecifiers = new SortedDictionary(); + _withAttributes = new SortedDictionary>(); + _withCallConvs = new SortedDictionary(); + _withClasses = new SortedDictionary(); + _withLibraryPaths = new SortedDictionary(); + _withNamespaces = new SortedDictionary(); + _withTransparentStructs = new SortedDictionary(); + _withTypes = new SortedDictionary(); + _withUsings = new SortedDictionary>(); - _options = options; - _forceRemappedNames = new HashSet(); - _remappedNames = new Dictionary(); - _withAccessSpecifiers = new Dictionary(); - _withAttributes = new Dictionary>(); - _withCallConvs = new Dictionary(); - _withClasses = new Dictionary(); - _withLibraryPaths = new Dictionary(); - _withNamespaces = new Dictionary(); - _withTransparentStructs = new Dictionary(); - _withTypes = new Dictionary(); - _withUsings = new Dictionary>(); - - ExcludedNames = excludedNames; HeaderText = string.IsNullOrWhiteSpace(headerFile) ? string.Empty : File.ReadAllText(headerFile); - IncludedNames = includedNames; LibraryPath = $@"""{libraryPath}"""; DefaultClass = methodClassName; MethodPrefixToStrip = methodPrefixToStrip; @@ -117,10 +108,6 @@ public PInvokeGeneratorConfiguration(string libraryPath, string namespaceName, s OutputLocation = Path.GetFullPath(outputLocation); TestOutputLocation = !string.IsNullOrWhiteSpace(testOutputLocation) ? Path.GetFullPath(testOutputLocation) : string.Empty; - // Normalize the traversal names to use \ rather than / so path comparisons are simpler - TraversalNames = traversalNames.Select(traversalName => traversalName.Replace('\\', '/')).ToArray(); - WithSetLastErrors = withSetLastErrors; - if (!_options.HasFlag(PInvokeGeneratorConfigurationOptions.NoDefaultRemappings)) { if (!ExcludeNIntCodegen) @@ -139,7 +126,13 @@ public PInvokeGeneratorConfiguration(string libraryPath, string namespaceName, s } } + AddRange(_excludedNames, excludedNames); AddRange(_forceRemappedNames, remappedNames, ValueStartsWithAt); + AddRange(_includedNames, includedNames); + AddRange(_traversalNames, traversalNames, NormalizePathSeparators); + AddRange(_withSetLastErrors, withSetLastErrors); + AddRange(_withSuppressGCTransitions, withSuppressGCTransitions); + AddRange(_remappedNames, remappedNames, RemoveAtPrefix); AddRange(_withAccessSpecifiers, withAccessSpecifiers, ConvertStringToAccessSpecifier); AddRange(_withAttributes, withAttributes); @@ -160,14 +153,16 @@ public PInvokeGeneratorConfiguration(string libraryPath, string namespaceName, s public bool ExcludeEnumOperators => _options.HasFlag(PInvokeGeneratorConfigurationOptions.ExcludeEnumOperators); - public string[] ExcludedNames { get; } + public IReadOnlyCollection ExcludedNames => _excludedNames; - public string[] IncludedNames { get; } + public IReadOnlyCollection IncludedNames => _includedNames; public bool GenerateAggressiveInlining => _options.HasFlag(PInvokeGeneratorConfigurationOptions.GenerateAggressiveInlining); public bool GenerateCompatibleCode => _options.HasFlag(PInvokeGeneratorConfigurationOptions.GenerateCompatibleCode); + public bool GenerateDocIncludes => _options.HasFlag(PInvokeGeneratorConfigurationOptions.GenerateDocIncludes); + public bool GenerateExplicitVtbls => _options.HasFlag(PInvokeGeneratorConfigurationOptions.GenerateExplicitVtbls); public bool GenerateFileScopedNamespaces => _options.HasFlag(PInvokeGeneratorConfigurationOptions.GenerateFileScopedNamespaces); @@ -254,7 +249,7 @@ public bool ExcludeFnptrCodegen public string TestOutputLocation { get; } - public string[] TraversalNames { get; } + public IReadOnlyCollection TraversalNames => _traversalNames; public IReadOnlyDictionary WithAccessSpcifier => _withAccessSpecifiers; @@ -268,7 +263,9 @@ public bool ExcludeFnptrCodegen public IReadOnlyDictionary WithNamespaces => _withNamespaces; - public string[] WithSetLastErrors { get; } + public IReadOnlyCollection WithSetLastErrors => _withSetLastErrors; + + public IReadOnlyCollection WithSuppressGCTransitions => _withSuppressGCTransitions; public IReadOnlyDictionary WithTransparentStructs => _withTransparentStructs; @@ -276,7 +273,7 @@ public bool ExcludeFnptrCodegen public IReadOnlyDictionary> WithUsings => _withUsings; - private static void AddRange(Dictionary dictionary, IEnumerable> keyValuePairs) + private static void AddRange(SortedDictionary dictionary, IEnumerable> keyValuePairs) { if (keyValuePairs != null) { @@ -289,29 +286,51 @@ private static void AddRange(Dictionary dictionary, IEnu } } - private static void AddRange(HashSet hashSet, IEnumerable> keyValuePairs, Func shouldAdd) + private static void AddRange(SortedDictionary dictionary, IEnumerable> keyValuePairs, Func convert) { if (keyValuePairs != null) { foreach (var keyValuePair in keyValuePairs) { - if (shouldAdd(keyValuePair.Value)) - { - _ = hashSet.Add(keyValuePair.Key); - } + // Use the indexer, rather than Add, so that any + // default mappings can be overwritten if desired. + dictionary[keyValuePair.Key] = convert(keyValuePair.Value); } } } - private static void AddRange(Dictionary dictionary, IEnumerable> keyValuePairs, Func convert) + private static void AddRange(SortedSet hashSet, IEnumerable keys) + { + if (keys != null) + { + foreach (var key in keys) + { + _ = hashSet.Add(key); + } + } + } + + private static void AddRange(SortedSet hashSet, IEnumerable keys, Func convert) + { + if (keys != null) + { + foreach (var key in keys) + { + _ = hashSet.Add(convert(key)); + } + } + } + + private static void AddRange(SortedSet hashSet, IEnumerable> keyValuePairs, Func shouldAdd) { if (keyValuePairs != null) { foreach (var keyValuePair in keyValuePairs) { - // Use the indexer, rather than Add, so that any - // default mappings can be overwritten if desired. - dictionary[keyValuePair.Key] = convert(keyValuePair.Value); + if (shouldAdd(keyValuePair.Value)) + { + _ = hashSet.Add(keyValuePair.Key); + } } } } @@ -348,6 +367,8 @@ private static AccessSpecifier ConvertStringToAccessSpecifier(string input) } } + private static string NormalizePathSeparators(string value) => value.Replace('\\', '/'); + private static string RemoveAtPrefix(string value) => ValueStartsWithAt(value) ? value[1..] : value; private static (string, PInvokeGeneratorTransparentStructKind) RemoveAtPrefix((string Name, PInvokeGeneratorTransparentStructKind Kind) value) => (ValueStartsWithAt(value.Name) ? value.Name[1..] : value.Name, value.Kind); diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGeneratorConfigurationOptions.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGeneratorConfigurationOptions.cs index 8e682f15..60605f91 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGeneratorConfigurationOptions.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGeneratorConfigurationOptions.cs @@ -72,5 +72,7 @@ public enum PInvokeGeneratorConfigurationOptions : ulong GenerateFileScopedNamespaces = 1UL << 30, GenerateSetsLastSystemErrorAttribute = 1UL << 31, + + GenerateDocIncludes = 1UL << 32, } } diff --git a/sources/ClangSharpPInvokeGenerator/Program.cs b/sources/ClangSharpPInvokeGenerator/Program.cs index a53a96df..d970068d 100644 --- a/sources/ClangSharpPInvokeGenerator/Program.cs +++ b/sources/ClangSharpPInvokeGenerator/Program.cs @@ -5,7 +5,6 @@ using System.CommandLine; using System.CommandLine.Help; using System.CommandLine.Invocation; -using System.CommandLine.IO; using System.IO; using System.Linq; using System.Runtime.InteropServices; @@ -66,6 +65,7 @@ public class Program new HelpItem("generate-aggressive-inlining", "[MethodImpl(MethodImplOptions.AggressiveInlining)] should be added to generated helper functions."), new HelpItem("generate-cpp-attributes", "[CppAttributeList(\"\")] should be generated to document the encountered C++ attributes."), + new HelpItem("generate-doc-includes", " xml documentation tags should be generated for declarations."), new HelpItem("generate-file-scoped-namespaces", "Namespaces should be scoped to the file to reduce nesting."), new HelpItem("generate-helper-types", "Code files should be generated for various helper attributes and declared transparent structs."), new HelpItem("generate-macro-bindings", "Bindings for macro-definitions should be generated. This currently only works with value like macros and not function-like ones."), @@ -119,6 +119,7 @@ public static async Task Main(params string[] args) AddWithLibraryPathOption(s_rootCommand); AddWithNamespaceOption(s_rootCommand); AddWithSetLastErrorOption(s_rootCommand); + AddWithSuppressGCTransitionOption(s_rootCommand); AddWithTransparentStructOption(s_rootCommand); AddWithTypeOption(s_rootCommand); AddWithUsingOption(s_rootCommand); @@ -156,6 +157,7 @@ public static int Run(InvocationContext context) var withLibraryPathNameValuePairs = context.ParseResult.ValueForOption("--with-librarypath"); var withNamespaceNameValuePairs = context.ParseResult.ValueForOption("--with-namespace"); var withSetLastErrors = context.ParseResult.ValueForOption("--with-setlasterror"); + var withSuppressGCTransitions = context.ParseResult.ValueForOption("--with-suppressgctransition"); var withTransparentStructNameValuePairs = context.ParseResult.ValueForOption("--with-transparent-struct"); var withTypeNameValuePairs = context.ParseResult.ValueForOption("--with-type"); var withUsingNameValuePairs = context.ParseResult.ValueForOption("--with-using"); @@ -399,6 +401,12 @@ public static int Run(InvocationContext context) break; } + case "generate-doc-includes": + { + configOptions |= PInvokeGeneratorConfigurationOptions.GenerateDocIncludes; + break; + } + case "generate-file-scoped-namespaces": { configOptions |= PInvokeGeneratorConfigurationOptions.GenerateFileScopedNamespaces; @@ -548,7 +556,7 @@ public static int Run(InvocationContext context) translationFlags |= CXTranslationUnit_Flags.CXTranslationUnit_IncludeAttributedTypes; // Include attributed types in CXType translationFlags |= CXTranslationUnit_Flags.CXTranslationUnit_VisitImplicitAttributes; // Implicit attributes should be visited - var config = new PInvokeGeneratorConfiguration(libraryPath, namespaceName, outputLocation, testOutputLocation, outputMode, configOptions, excludedNames, includedNames, headerFile, methodClassName, methodPrefixToStrip, remappedNames, traversalNames, withAccessSpecifiers, withAttributes, withCallConvs, withClasses, withLibraryPath, withNamespaces, withSetLastErrors, withTransparentStructs, withTypes, withUsings); + var config = new PInvokeGeneratorConfiguration(libraryPath, namespaceName, outputLocation, testOutputLocation, outputMode, configOptions, excludedNames, includedNames, headerFile, methodClassName, methodPrefixToStrip, remappedNames, traversalNames, withAccessSpecifiers, withAttributes, withCallConvs, withClasses, withLibraryPath, withNamespaces, withSetLastErrors, withSuppressGCTransitions, withTransparentStructs, withTypes, withUsings); if (config.GenerateMacroBindings) { @@ -1092,7 +1100,20 @@ private static void AddWithSetLastErrorOption(RootCommand rootCommand) { var option = new Option( aliases: new string[] { "--with-setlasterror", "-wsle" }, - description: "Add the SetLastError=true modifier to a given DllImport or UnmanagedFunctionPointer.", + description: "Add the SetLastError=true modifier or SetsSystemLastError attribute to a given DllImport or UnmanagedFunctionPointer.", + argumentType: typeof(string), + getDefaultValue: Array.Empty, + arity: ArgumentArity.OneOrMore + ); + + rootCommand.AddOption(option); + } + + private static void AddWithSuppressGCTransitionOption(RootCommand rootCommand) + { + var option = new Option( + aliases: new string[] { "--with-suppressgctransition", "-wsgct" }, + description: "Add the SuppressGCTransition calling convention to a given DllImport or UnmanagedFunctionPointer.", argumentType: typeof(string), getDefaultValue: Array.Empty, arity: ArgumentArity.OneOrMore diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/PInvokeGeneratorTest.cs b/tests/ClangSharp.PInvokeGenerator.UnitTests/PInvokeGeneratorTest.cs index 642f9bf6..c7e4e279 100644 --- a/tests/ClangSharp.PInvokeGenerator.UnitTests/PInvokeGeneratorTest.cs +++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/PInvokeGeneratorTest.cs @@ -64,7 +64,7 @@ private static async Task ValidateGeneratedBindingsAsync(string inputContents, s using var unsavedFile = CXUnsavedFile.Create(DefaultInputFileName, inputContents); var unsavedFiles = new CXUnsavedFile[] { unsavedFile }; - var config = new PInvokeGeneratorConfiguration(libraryPath, DefaultNamespaceName, Path.GetRandomFileName(), testOutputLocation: null, outputMode, configOptions, excludedNames, includedNames: null, headerFile: null, methodClassName: null, methodPrefixToStrip: null, remappedNames, traversalNames: null, withAccessSpecifiers, withAttributes, withCallConvs, withClasses, withLibraryPaths, withNamespaces, withSetLastErrors, withTransparentStructs, withTypes, withUsings); + var config = new PInvokeGeneratorConfiguration(libraryPath, DefaultNamespaceName, Path.GetRandomFileName(), testOutputLocation: null, outputMode, configOptions, excludedNames, includedNames: null, headerFile: null, methodClassName: null, methodPrefixToStrip: null, remappedNames, traversalNames: null, withAccessSpecifiers, withAttributes, withCallConvs, withClasses, withLibraryPaths, withNamespaces, withSetLastErrors, withSuppressGCTransitions: null, withTransparentStructs, withTypes, withUsings); using (var pinvokeGenerator = new PInvokeGenerator(config, (path) => outputStream)) {