From 3c06ebae9536376c4a088592762a1ccff5aa2253 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20H=C3=B8is=C3=A6ther=20Rasch?= Date: Tue, 21 May 2024 15:21:43 +0200 Subject: [PATCH] Add support for C/C++ attribute annotate and emit custom attribute type if encountered. --- .../PInvokeGenerator.cs | 99 +++++++++++++++++++ 1 file changed, 99 insertions(+) diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs index 841aeb15..ffbace62 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs @@ -65,6 +65,8 @@ public sealed partial class PInvokeGenerator : IDisposable private readonly HashSet _usedRemappings; private readonly string _placeholderMacroType; + private bool _hasAnnotateAttr; + private string _filePath; private string[] _clangCommandLineArgs; private CXTranslationUnit_Flags _translationFlags; @@ -435,6 +437,7 @@ public void Close() GenerateNativeBitfieldAttribute(this, stream, leaveStreamOpen); GenerateNativeInheritanceAttribute(this, stream, leaveStreamOpen); GenerateNativeTypeNameAttribute(this, stream, leaveStreamOpen); + GenerateNativeAnnotationAttribute(this, stream, leaveStreamOpen); GenerateSetsLastSystemErrorAttribute(this, stream, leaveStreamOpen); GenerateVtblIndexAttribute(this, stream, leaveStreamOpen); GenerateTransparentStructs(this, stream, leaveStreamOpen); @@ -795,6 +798,94 @@ static void GenerateNativeTypeNameAttribute(PInvokeGenerator generator, Stream? } } + static void GenerateNativeAnnotationAttribute(PInvokeGenerator generator, Stream? stream, bool leaveStreamOpen) + { + var config = generator.Config; + + if (!generator._hasAnnotateAttr) + { + return; + } + + if (stream is null) + { + var outputPath = Path.Combine(config.OutputLocation, "NativeAnnotationAttribute.cs"); + stream = generator._outputStreamFactory(outputPath); + } + + using var sw = new StreamWriter(stream, s_defaultStreamWriterEncoding, DefaultStreamWriterBufferSize, leaveStreamOpen); + sw.NewLine = "\n"; + + if (!string.IsNullOrEmpty(config.HeaderText)) + { + sw.WriteLine(config.HeaderText); + } + + var indentString = " "; + + sw.WriteLine("using System;"); + sw.WriteLine("using System.Diagnostics;"); + sw.WriteLine(); + + sw.Write("namespace "); + sw.Write(generator.GetNamespace("NativeAnnotationAttribute")); + + if (generator.Config.GenerateFileScopedNamespaces) + { + sw.WriteLine(';'); + sw.WriteLine(); + indentString = ""; + } + else + { + sw.WriteLine(); + sw.WriteLine('{'); + } + + sw.Write(indentString); + sw.WriteLine("/// Defines the annotation found in a native declaration."); + sw.Write(indentString); + sw.WriteLine("[AttributeUsage(AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.ReturnValue, AllowMultiple = true, Inherited = false)]"); + sw.Write(indentString); + sw.WriteLine("[Conditional(\"DEBUG\")]"); + sw.Write(indentString); + sw.WriteLine("internal sealed partial class NativeAnnotationAttribute : Attribute"); + sw.Write(indentString); + sw.WriteLine('{'); + sw.Write(indentString); + sw.WriteLine(" private readonly string _annotation;"); + sw.WriteLine(); + sw.Write(indentString); + sw.WriteLine(" /// Initializes a new instance of the class."); + sw.Write(indentString); + sw.WriteLine(" /// The annotation that was used in the native declaration."); + sw.Write(indentString); + sw.WriteLine(" public NativeAnnotationAttribute(string annotation)"); + sw.Write(indentString); + sw.WriteLine(" {"); + sw.Write(indentString); + sw.WriteLine(" _annotation = annotation;"); + sw.Write(indentString); + sw.WriteLine(" }"); + sw.WriteLine(); + sw.Write(indentString); + sw.WriteLine(" /// Gets the annotation that was used in the native declaration."); + sw.Write(indentString); + sw.WriteLine(" public string Annotation => _annotation;"); + sw.Write(indentString); + sw.WriteLine('}'); + + if (!generator.Config.GenerateFileScopedNamespaces) + { + sw.WriteLine('}'); + } + + if (!leaveStreamOpen) + { + stream = null; + } + } + static void GenerateSetsLastSystemErrorAttribute(PInvokeGenerator generator, Stream? stream, bool leaveStreamOpen) { var config = generator.Config; @@ -6724,6 +6815,14 @@ private void WithAttributes(NamedDecl namedDecl, bool onlySupportedOSPlatform = break; } + case CX_AttrKind_Annotate: + { + _hasAnnotateAttr = true; + var annotationText = attr.Spelling; + outputBuilder.WriteCustomAttribute($"""NativeAnnotation("{annotationText}")"""); + break; + } + case CX_AttrKind_Format: case CX_AttrKind_FormatArg: case CX_AttrKind_MSNoVTable: