Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle attributes for class remappings #292

Merged
merged 1 commit into from
Nov 15, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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