Skip to content

Commit

Permalink
Merge pull request #292 from tannergooding/main
Browse files Browse the repository at this point in the history
Handle attributes for class remappings
  • Loading branch information
tannergooding authored Nov 15, 2021
2 parents e55a56a + b4eb40c commit 0f2b740
Show file tree
Hide file tree
Showing 2 changed files with 147 additions and 16 deletions.
86 changes: 77 additions & 9 deletions sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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("/// <summary>Provides validation of the <see cref=\"");
_testOutputBuilder.Write(escapedName);
Expand Down Expand Up @@ -1213,8 +1222,7 @@ private void VisitRecordDecl(RecordDecl recordDecl)
baseTypeNames = baseTypeNamesBuilder.ToArray();
}

var desc = new StructDesc<(string Name, PInvokeGenerator This)>
{
var desc = new StructDesc<(string Name, PInvokeGenerator This)> {
AccessSpecifier = GetAccessSpecifier(recordDecl),
EscapedName = escapedName,
IsUnsafe = IsUnsafe(recordDecl),
Expand All @@ -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<string>();
}

if (!_topLevelClassUsings.TryGetValue(name, out var withUsings))
{
withUsings = new HashSet<string>();
}

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)
{
Expand Down Expand Up @@ -1469,11 +1534,14 @@ private void VisitRecordDecl(RecordDecl recordDecl)
}
}

_outputBuilder.EndStruct();

if (generateTestsClass)
if (!isTopLevelStruct)
{
_testOutputBuilder.WriteBlockEnd();
_outputBuilder.EndStruct();

if (generateTestsClass)
{
_testOutputBuilder.WriteBlockEnd();
}
}
}
StopUsingOutputBuilder();
Expand Down
77 changes: 70 additions & 7 deletions sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ public sealed partial class PInvokeGenerator : IDisposable
private readonly Dictionary<CXXMethodDecl, uint> _overloadIndices;
private readonly Dictionary<Cursor, uint> _isExcluded;
private readonly Dictionary<string, bool> _isTopLevelClassUnsafe;
private readonly Dictionary<string, HashSet<string>> _topLevelClassUsings;
private readonly Dictionary<string, List<string>> _topLevelClassAttributes;
private readonly HashSet<string> _topLevelClassNames;
private readonly HashSet<string> _usedRemappings;

Expand Down Expand Up @@ -108,6 +110,8 @@ public PInvokeGenerator(PInvokeGeneratorConfiguration config, Func<string, Strea
_isExcluded = new Dictionary<Cursor, uint>();
_isTopLevelClassUnsafe = new Dictionary<string, bool>();
_topLevelClassNames = new HashSet<string>();
_topLevelClassAttributes = new Dictionary<string, List<string>>();
_topLevelClassUsings = new Dictionary<string, HashSet<string>>();
_usedRemappings = new HashSet<string>();
}

Expand Down Expand Up @@ -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())
Expand Down Expand Up @@ -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("/// <summary>Provides validation of the <see cref=\"");
sw.Write(nonTestName);
sw.WriteLine("\" /> class.</summary>");
sw.Write(indentationString);
sw.Write("\" /> ");

if (isTopLevelStruct)
{
sw.Write("struct");
}
else
{
sw.Write("class");
}

sw.WriteLine(".</summary>");
}

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();
}
Expand Down

0 comments on commit 0f2b740

Please sign in to comment.