Skip to content
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
8 changes: 4 additions & 4 deletions sources/ClangSharp.Interop/Extensions/CXCursor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,16 @@ public string AttrKindSpelling
{
Debug.Assert(CX_AttrKind.CX_AttrKind_FirstDeclOrTypeAttr == CX_AttrKind.CX_AttrKind_AArch64VectorPcs);
Debug.Assert(CX_AttrKind.CX_AttrKind_LastDeclOrTypeAttr == CX_AttrKind.CX_AttrKind_VectorCall);
Debug.Assert(CX_AttrKind.CX_AttrKind_FirstInheritableParamAttr == CX_AttrKind.CX_AttrKind_SwiftContext);
Debug.Assert(CX_AttrKind.CX_AttrKind_FirstInheritableParamAttr == CX_AttrKind.CX_AttrKind_SwiftAsyncContext);
Debug.Assert(CX_AttrKind.CX_AttrKind_LastInheritableParamAttr == CX_AttrKind.CX_AttrKind_UseHandle);
Debug.Assert(CX_AttrKind.CX_AttrKind_FirstParameterABIAttr == CX_AttrKind.CX_AttrKind_SwiftContext);
Debug.Assert(CX_AttrKind.CX_AttrKind_FirstParameterABIAttr == CX_AttrKind.CX_AttrKind_SwiftAsyncContext);
Debug.Assert(CX_AttrKind.CX_AttrKind_LastParameterABIAttr == CX_AttrKind.CX_AttrKind_SwiftIndirectResult);
Debug.Assert(CX_AttrKind.CX_AttrKind_LastAttr == CX_AttrKind.CX_AttrKind_Thread);
Debug.Assert(CX_AttrKind.CX_AttrKind_FirstTypeAttr == CX_AttrKind.CX_AttrKind_AddressSpace);
Debug.Assert(CX_AttrKind.CX_AttrKind_LastTypeAttr == CX_AttrKind.CX_AttrKind_UPtr);
Debug.Assert(CX_AttrKind.CX_AttrKind_FirstStmtAttr == CX_AttrKind.CX_AttrKind_FallThrough);
Debug.Assert(CX_AttrKind.CX_AttrKind_LastStmtAttr == CX_AttrKind.CX_AttrKind_Suppress);
Debug.Assert(CX_AttrKind.CX_AttrKind_FirstInheritableAttr == CX_AttrKind.CX_AttrKind_AArch64VectorPcs);
Debug.Assert(CX_AttrKind.CX_AttrKind_LastStmtAttr == CX_AttrKind.CX_AttrKind_Unlikely);
Debug.Assert(CX_AttrKind.CX_AttrKind_FirstInheritableAttr == CX_AttrKind.CX_AttrKind_NoMerge);
Debug.Assert(CX_AttrKind.CX_AttrKind_LastInheritableAttr == CX_AttrKind.CX_AttrKind_XRayLogArgs);

return AttrKind switch
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ internal enum ValueKind
Enumerator,
Unmanaged,
String,
GuidMember,
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,13 @@ public void BeginValue(in ValueDesc desc)
Write(desc.TypeName);
Write(' ');
}
else if (desc.Kind == ValueKind.GuidMember)
{
Write("static ");
Write(desc.TypeName);
Write(' ');
isExpressionBody = true;
}

Write(desc.EscapedName);

Expand Down Expand Up @@ -215,6 +222,13 @@ public void EndValue(in ValueDesc desc)
WriteLine(';');
break;
}

case ValueKind.GuidMember:
{
WriteLine(';');
NeedsNewline = true;
break;
}
}
}

Expand Down Expand Up @@ -755,11 +769,20 @@ public void BeginStruct(in StructDesc desc)
Write("partial struct ");
Write(desc.EscapedName);

if (desc.HasVtbl && _config.GenerateMarkerInterfaces)
if (_config.GenerateMarkerInterfaces)
{
Write(" : ");
Write(desc.EscapedName);
Write(".Interface");
if (desc.HasVtbl)
{
Write(" : ");
Write(desc.EscapedName);
Write(".Interface");
}

if ((desc.Uuid is not null) && _config.GenerateGuidMember && _config.GeneratePreviewCode)
{
Write(desc.HasVtbl ? ", " : " : ");
Write("INativeGuid");
}
}

WriteNewline();
Expand Down
57 changes: 47 additions & 10 deletions sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -567,9 +567,9 @@ private void VisitFunctionDecl(FunctionDecl functionDecl)
CustomAttrGeneratorData = (functionDecl, _outputBuilder, this),
};

_ = _isTopLevelClassUnsafe.TryGetValue(className, out var isUnsafe);
_ = _topLevelClassIsUnsafe.TryGetValue(className, out var isUnsafe);
_outputBuilder.BeginFunctionOrDelegate(in desc, ref isUnsafe);
_isTopLevelClassUnsafe[className] = isUnsafe;
_topLevelClassIsUnsafe[className] = isUnsafe;

_outputBuilder.BeginFunctionInnerPrototype(in desc);

Expand Down Expand Up @@ -1255,17 +1255,19 @@ private void VisitRecordDecl(RecordDecl recordDecl)
_testOutputBuilder.WriteBlockStart();
}

Guid? nullableUuid = null;
var nullableUuid = (Guid?)null;
var uuidName = "";

if (TryGetUuid(recordDecl, out var uuid))
{
nullableUuid = uuid;
var iidName = GetRemappedName($"IID_{nativeName}", recordDecl, tryRemapOperatorName: false, out var wasRemapped, skipUsing: true);
uuidName = GetRemappedName($"IID_{nativeName}", recordDecl, tryRemapOperatorName: false, out var wasRemapped, skipUsing: true);

_uuidsToGenerate.Add(iidName, uuid);
_uuidsToGenerate.Add(uuidName, uuid);

if (_testOutputBuilder != null)
{
var className = GetClass(iidName);
var className = GetClass(uuidName);

_testOutputBuilder.AddUsingDirective("System");
_testOutputBuilder.AddUsingDirective($"static {GetNamespace(className)}.{className}");
Expand Down Expand Up @@ -1297,7 +1299,7 @@ private void VisitRecordDecl(RecordDecl recordDecl)
_testOutputBuilder.Write("Is.EqualTo(");
}

_testOutputBuilder.Write(iidName);
_testOutputBuilder.Write(uuidName);

if (_config.GenerateTestsNUnit)
{
Expand All @@ -1312,6 +1314,8 @@ private void VisitRecordDecl(RecordDecl recordDecl)
}
}

var hasGuidMember = _config.GenerateGuidMember && !string.IsNullOrWhiteSpace(uuidName);

var layoutKind = recordDecl.IsUnion
? LayoutKind.Explicit
: LayoutKind.Sequential;
Expand Down Expand Up @@ -1355,7 +1359,7 @@ private void VisitRecordDecl(RecordDecl recordDecl)
var desc = new StructDesc {
AccessSpecifier = GetAccessSpecifier(recordDecl),
EscapedName = escapedName,
IsUnsafe = IsUnsafe(recordDecl),
IsUnsafe = IsUnsafe(recordDecl) || hasGuidMember,
HasVtbl = hasVtbl || hasBaseVtbl,
IsUnion = recordDecl.IsUnion,
Layout = new() {
Expand Down Expand Up @@ -1440,10 +1444,43 @@ private void VisitRecordDecl(RecordDecl recordDecl)

if (desc.IsUnsafe)
{
_isTopLevelClassUnsafe[name] = true;
_topLevelClassIsUnsafe[name] = true;
}

if (hasGuidMember)
{
_topLevelClassHasGuidMember[name] = true;
}
}

if (hasGuidMember)
{
var valueDesc = new ValueDesc {
AccessSpecifier = AccessSpecifier.None,
TypeName = "Guid*",
EscapedName = "INativeGuid.NativeGuid",
ParentName = name,
Kind = ValueKind.GuidMember,
Flags = ValueFlags.Initializer,
};

var uuidClassName = GetClass(uuidName);

_outputBuilder.EmitUsingDirective("System");
_outputBuilder.EmitUsingDirective("System.Runtime.CompilerServices");

_outputBuilder.EmitUsingDirective($"static {GetNamespace(uuidClassName)}.{uuidClassName}");
_outputBuilder.BeginValue(in valueDesc);

var code = _outputBuilder.BeginCSharpCode();
code.Write("(Guid*)Unsafe.AsPointer(ref Unsafe.AsRef(in ");
code.Write(uuidName);
code.Write("))");
_outputBuilder.EndCSharpCode(code);

_outputBuilder.EndValue(in valueDesc);
}

if (hasVtbl || (hasBaseVtbl && !HasBaseField(cxxRecordDecl)))
{
var fieldDesc = new FieldDesc {
Expand Down Expand Up @@ -3091,7 +3128,7 @@ private void VisitVarDecl(VarDecl varDecl)

if (IsUnsafe(varDecl, type) && (!varDecl.HasInit || !IsStmtAsWritten<StringLiteral>(varDecl.Init, out _, removeParens: true)))
{
_isTopLevelClassUnsafe[className] = true;
_topLevelClassIsUnsafe[className] = true;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2296,7 +2296,8 @@ private void VisitStringLiteral(StringLiteral stringLiteral)
{
outputBuilder.Write('"');
outputBuilder.Write(EscapeString(stringLiteral.String));
outputBuilder.Write("\\0\"u8");
outputBuilder.Write('"');
outputBuilder.Write("u8");
}
else
{
Expand Down Expand Up @@ -2380,7 +2381,7 @@ private void VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr unaryExprOrT
{
if (_topLevelClassNames.Contains(_outputBuilder.Name))
{
_isTopLevelClassUnsafe[_outputBuilder.Name] = true;
_topLevelClassIsUnsafe[_outputBuilder.Name] = true;
}

var parentType = null as Type;
Expand Down
81 changes: 75 additions & 6 deletions sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ public sealed partial class PInvokeGenerator : IDisposable
private readonly Dictionary<string, HashSet<string>> _traversedValidNameRemappings;
private readonly Dictionary<CXXMethodDecl, uint> _overloadIndices;
private readonly Dictionary<Cursor, uint> _isExcluded;
private readonly Dictionary<string, bool> _isTopLevelClassUnsafe;
private readonly Dictionary<string, bool> _topLevelClassHasGuidMember;
private readonly Dictionary<string, bool> _topLevelClassIsUnsafe;
private readonly Dictionary<string, HashSet<string>> _topLevelClassUsings;
private readonly Dictionary<string, List<string>> _topLevelClassAttributes;
private readonly HashSet<string> _topLevelClassNames;
Expand Down Expand Up @@ -111,7 +112,8 @@ public PInvokeGenerator(PInvokeGeneratorConfiguration config, Func<string, Strea
_traversedValidNameRemappings = new Dictionary<string, HashSet<string>>();
_overloadIndices = new Dictionary<CXXMethodDecl, uint>();
_isExcluded = new Dictionary<Cursor, uint>();
_isTopLevelClassUnsafe = new Dictionary<string, bool>();
_topLevelClassHasGuidMember = new Dictionary<string, bool>();
_topLevelClassIsUnsafe = new Dictionary<string, bool>();
_topLevelClassNames = new HashSet<string>();
_topLevelClassAttributes = new Dictionary<string, List<string>>();
_topLevelClassUsings = new Dictionary<string, HashSet<string>>();
Expand Down Expand Up @@ -280,6 +282,9 @@ public void Close()
{
foreach (var entry in methodClassOutputBuilders)
{
var hasGuidMember = _config.GenerateGuidMember && _config.GeneratePreviewCode;
hasGuidMember &= _uuidsToGenerate.ContainsKey(entry.Value.Name) || _generatedUuids.Contains(entry.Value.Name);

CloseOutputBuilder(stream, entry.Value, isMethodClass: true, leaveStreamOpen, emitNamespaceDeclaration);
}

Expand Down Expand Up @@ -631,7 +636,7 @@ static void GenerateTransparentStructs(PInvokeGenerator generator)

sw.WriteLine();

sw.Write("namespace ");sw.WriteLine();
sw.Write("namespace ");
sw.Write(targetNamespace);

if (generator.Config.GenerateFileScopedNamespaces)
Expand All @@ -647,7 +652,7 @@ static void GenerateTransparentStructs(PInvokeGenerator generator)
}

sw.Write(indentString);
sw.Write("public ");
sw.Write("public readonly ");

if (isTypePointer || IsTransparentStructHexBased(kind))
{
Expand Down Expand Up @@ -1556,7 +1561,7 @@ void ForCSharp(CSharpOutputBuilder csharpOutputBuilder)
sw.Write("static ");
}

if ((_isTopLevelClassUnsafe.TryGetValue(nonTestName, out var isUnsafe) && isUnsafe) || (outputBuilder.IsTestOutput && isTopLevelStruct))
if ((_topLevelClassIsUnsafe.TryGetValue(nonTestName, out var isUnsafe) && isUnsafe) || (outputBuilder.IsTestOutput && isTopLevelStruct))
{
sw.Write("unsafe ");
}
Expand All @@ -1574,6 +1579,11 @@ void ForCSharp(CSharpOutputBuilder csharpOutputBuilder)

sw.Write(outputBuilder.Name);

if (_topLevelClassHasGuidMember.TryGetValue(outputBuilder.Name, out var hasGuidMember) && hasGuidMember)
{
sw.Write(" : INativeGuid");
}

sw.WriteLine();
sw.Write(indentationString);
sw.Write('{');
Expand Down Expand Up @@ -1635,7 +1645,7 @@ void ForXml(XmlOutputBuilder xmlOutputBuilder)
sw.Write(xmlOutputBuilder.Name);
sw.Write("\" access=\"public\" static=\"true\"");

if (_isTopLevelClassUnsafe.TryGetValue(xmlOutputBuilder.Name, out var isUnsafe) && isUnsafe)
if (_topLevelClassIsUnsafe.TryGetValue(xmlOutputBuilder.Name, out var isUnsafe) && isUnsafe)
{
sw.Write(" unsafe=\"true\"");
}
Expand Down Expand Up @@ -5388,6 +5398,12 @@ private void StopUsingOutputBuilder()

private bool TryGetUuid(RecordDecl recordDecl, out Guid uuid)
{
if (TryGetRemappedValue(recordDecl, _config.WithGuids, out var guid))
{
uuid = guid;
return true;
}

var uuidAttrs = recordDecl.Attrs.Where((attr) => attr.Kind == CX_AttrKind.CX_AttrKind_Uuid);

if (!uuidAttrs.Any())
Expand Down Expand Up @@ -5682,6 +5698,59 @@ private void WithAttributes(NamedDecl namedDecl, bool onlySupportedOSPlatform =
outputBuilder.WriteCustomAttribute(attribute);
}
}

if (!isTestOutput && namedDecl.HasAttrs)
{
foreach (var attr in namedDecl.Attrs)
{
switch (attr.Kind)
{
case CX_AttrKind.CX_AttrKind_Aligned:
case CX_AttrKind.CX_AttrKind_AlwaysInline:
case CX_AttrKind.CX_AttrKind_DLLExport:
case CX_AttrKind.CX_AttrKind_DLLImport:
{
// Nothing to handle
break;
}

case CX_AttrKind.CX_AttrKind_Deprecated:
{
var attrText = GetSourceRangeContents(namedDecl.TranslationUnit.Handle, attr.Extent);

var textStart = attrText.IndexOf('"');
var textLength = attrText.LastIndexOf('"') - textStart;

if (textLength > 2)
{
var text = attrText.AsSpan(textStart + 1, textLength - 2);
outputBuilder.WriteCustomAttribute($"Obsolete(\"{text}\")");
}
else
{
outputBuilder.WriteCustomAttribute($"Obsolete");
}
break;
}

case CX_AttrKind.CX_AttrKind_MSNoVTable:
case CX_AttrKind.CX_AttrKind_MSAllocator:
case CX_AttrKind.CX_AttrKind_MaxFieldAlignment:
case CX_AttrKind.CX_AttrKind_SelectAny:
case CX_AttrKind.CX_AttrKind_Uuid:
{
// Nothing to handle
break;
}

default:
{
AddDiagnostic(DiagnosticLevel.Warning, $"Unsupported attribute: '{attr.KindSpelling}'. Generated bindings may be incomplete.", namedDecl);
break;
}
}
}
}
}

private string GetLibraryPath(string remappedName)
Expand Down
Loading