From b4eb40c2d164601d38a028d19cf04632be1e8869 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Sun, 14 Nov 2021 17:29:09 -0800 Subject: [PATCH] Handle attributes for class remappings --- .../PInvokeGenerator.VisitDecl.cs | 86 +++++++++++++++++-- .../PInvokeGenerator.cs | 77 +++++++++++++++-- 2 files changed, 147 insertions(+), 16 deletions(-) diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs index c524a5d9..a95f8489 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Globalization; using System.Linq; using System.Runtime.InteropServices; using System.Text; @@ -319,7 +320,14 @@ private void VisitEnumDecl(EnumDecl enumDecl) if (name.StartsWith("__AnonymousEnum_")) { isAnonymousEnum = true; - name = GetClass(name); + + if (!TryGetClass(name, out var className, disallowPrefixMatch: true)) + { + className = _config.DefaultClass; + AddDiagnostic(DiagnosticLevel.Info, $"Found anonymous enum: {name}. Mapping values as constants in: {className}", enumDecl); + } + + name = className; } StartUsingOutputBuilder(name); @@ -1099,9 +1107,10 @@ private void VisitRecordDecl(RecordDecl recordDecl) var alignment = Math.Max(recordDecl.TypeForDecl.Handle.AlignOf, 1); var maxAlignm = recordDecl.Fields.Any() ? recordDecl.Fields.Max((fieldDecl) => Math.Max(fieldDecl.Type.Handle.AlignOf, 1)) : alignment; + var isTopLevelStruct = _config.WithTypes.TryGetValue(name, out var withType) && (withType == "struct"); var generateTestsClass = _testOutputBuilder != null && !recordDecl.IsAnonymousStructOrUnion && recordDecl.DeclContext is not RecordDecl; - if (generateTestsClass) + if (generateTestsClass && !isTopLevelStruct) { _testOutputBuilder.WriteIndented("/// Provides validation of the - { + var desc = new StructDesc<(string Name, PInvokeGenerator This)> { AccessSpecifier = GetAccessSpecifier(recordDecl), EscapedName = escapedName, IsUnsafe = IsUnsafe(recordDecl), @@ -1235,7 +1243,64 @@ private void VisitRecordDecl(RecordDecl recordDecl) Location = recordDecl.Location, IsNested = recordDecl.DeclContext is TagDecl, }; - _outputBuilder.BeginStruct(in desc); + + if (!isTopLevelStruct) + { + _outputBuilder.BeginStruct(in desc); + } + else + { + if (!_topLevelClassAttributes.TryGetValue(name, out var withAttributes)) + { + withAttributes = new List(); + } + + if (!_topLevelClassUsings.TryGetValue(name, out var withUsings)) + { + withUsings = new HashSet(); + } + + if (desc.LayoutAttribute is not null) + { + withAttributes.Add($"StructLayout(LayoutKind.{desc.LayoutAttribute.Value}{((desc.LayoutAttribute.Pack != 0) ? $", Pack = {desc.LayoutAttribute.Pack}" : "")})"); + _ = withUsings.Add("System.Runtime.InteropServices"); + } + + if (desc.Uuid is not null) + { + withAttributes.Add($"Guid(\"{nullableUuid.Value.ToString("D", CultureInfo.InvariantCulture).ToUpperInvariant()}\")"); + _ = withUsings.Add("System.Runtime.InteropServices"); + } + + if (desc.NativeType is not null) + { + withAttributes.Add($"NativeTypeName(\"{EscapeString(desc.NativeType)}\")"); + _ = withUsings.Add(GetNamespace("NativeTypeNameAttribute")); + } + + if (_config.GenerateNativeInheritanceAttribute && (desc.NativeInheritance is not null)) + { + withAttributes.Add($"NativeInheritance(\"{desc.NativeInheritance}\")"); + _ = withUsings.Add(GetNamespace("NativeInheritanceAttribute")); + } + + if (_config.GenerateSourceLocationAttribute && (desc.Location is not null)) + { + desc.Location.Value.GetFileLocation(out var file, out var line, out var column, out _); + withAttributes.Add($"SourceLocation(\"{EscapeString(file.Name.ToString())}\", {line}, {column})"); + _ = withUsings.Add(GetNamespace("SourceLocationAttribute")); + } + + if (withAttributes.Count != 0) + { + _topLevelClassAttributes[name] = withAttributes; + } + + if (withUsings.Count != 0) + { + _topLevelClassUsings[name] = withUsings; + } + } if (hasVtbl) { @@ -1469,11 +1534,14 @@ private void VisitRecordDecl(RecordDecl recordDecl) } } - _outputBuilder.EndStruct(); - - if (generateTestsClass) + if (!isTopLevelStruct) { - _testOutputBuilder.WriteBlockEnd(); + _outputBuilder.EndStruct(); + + if (generateTestsClass) + { + _testOutputBuilder.WriteBlockEnd(); + } } } StopUsingOutputBuilder(); diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs index 4874cb41..759b0a40 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs @@ -41,6 +41,8 @@ public sealed partial class PInvokeGenerator : IDisposable private readonly Dictionary _overloadIndices; private readonly Dictionary _isExcluded; private readonly Dictionary _isTopLevelClassUnsafe; + private readonly Dictionary> _topLevelClassUsings; + private readonly Dictionary> _topLevelClassAttributes; private readonly HashSet _topLevelClassNames; private readonly HashSet _usedRemappings; @@ -108,6 +110,8 @@ public PInvokeGenerator(PInvokeGeneratorConfiguration config, Func(); _isTopLevelClassUnsafe = new Dictionary(); _topLevelClassNames = new HashSet(); + _topLevelClassAttributes = new Dictionary>(); + _topLevelClassUsings = new Dictionary>(); _usedRemappings = new HashSet(); } @@ -1190,6 +1194,19 @@ private void CloseOutputBuilder(Stream stream, IOutputBuilder outputBuilder, boo sw.WriteLine(_config.HeaderText); } + if (isMethodClass) + { + var nonTestName = outputBuilder.IsTestOutput ? outputBuilder.Name[0..^5] : outputBuilder.Name; + + if (_topLevelClassUsings.TryGetValue(nonTestName, out var withUsings)) + { + foreach (var withUsing in withUsings) + { + csharpOutputBuilder.AddUsingDirective(withUsing); + } + } + } + var usingDirectives = csharpOutputBuilder.UsingDirectives.Concat(csharpOutputBuilder.StaticUsingDirectives); if (usingDirectives.Any()) @@ -1258,31 +1275,77 @@ void ForCSharp(CSharpOutputBuilder csharpOutputBuilder) if (isMethodClass) { - sw.Write(indentationString); + var isTopLevelStruct = _config.WithTypes.TryGetValue(nonTestName, out var withType) && (withType == "struct"); if (outputBuilder.IsTestOutput) { + sw.Write(indentationString); sw.Write("/// Provides validation of the class."); - sw.Write(indentationString); + sw.Write("\" /> "); + + if (isTopLevelStruct) + { + sw.Write("struct"); + } + else + { + sw.Write("class"); + } + + sw.WriteLine("."); } - sw.Write("public static "); + if (_topLevelClassAttributes.TryGetValue(nonTestName, out var withAttributes)) + { + if (withAttributes.Any()) + { + foreach (var attribute in withAttributes) + { + if (outputBuilder.IsTestOutput && !attribute.StartsWith("SupportedOSPlatform(")) + { + continue; + } + + sw.Write(indentationString); + sw.Write('['); + sw.Write(attribute); + sw.WriteLine(']'); + } + } + } - if (_isTopLevelClassUnsafe.TryGetValue(nonTestName, out var isUnsafe) && isUnsafe) + sw.Write(indentationString); + sw.Write("public "); + + if (outputBuilder.IsTestOutput || !isTopLevelStruct) + { + sw.Write("static "); + } + + if ((_isTopLevelClassUnsafe.TryGetValue(nonTestName, out var isUnsafe) && isUnsafe) || (outputBuilder.IsTestOutput && isTopLevelStruct)) { sw.Write("unsafe "); } - sw.Write("partial class "); + sw.Write("partial "); + + if (!outputBuilder.IsTestOutput && isTopLevelStruct) + { + sw.Write("struct "); + } + else + { + sw.Write("class "); + } + sw.Write(outputBuilder.Name); sw.WriteLine(); sw.Write(indentationString); sw.Write('{'); - if (!outputBuilder.IsTestOutput) + if ((!outputBuilder.IsTestOutput && !isTopLevelStruct) || !string.IsNullOrEmpty(csharpOutputBuilder.Contents.First())) { sw.WriteLine(); }