Skip to content
37 changes: 32 additions & 5 deletions sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -431,11 +431,13 @@ private void VisitFunctionDecl(FunctionDecl functionDecl)
type = attributedType.ModifiedType;
callConv = attributedType.Handle.FunctionTypeCallingConv;
}
var functionType = (FunctionType)type;

if (callConv == CXCallingConv.CXCallingConv_Invalid)
if (type is FunctionType functionType)
{
callConv = functionType.CallConv;
if (callConv == CXCallingConv.CXCallingConv_Invalid)
Comment on lines +435 to +437
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Simplify the nesting level here

Suggested change
if (type is FunctionType functionType)
{
callConv = functionType.CallConv;
if (callConv == CXCallingConv.CXCallingConv_Invalid)
if ((type is FunctionType functionType) && (callConv == CXCallingConv.CXCallingConv_Invalid))

{
callConv = functionType.CallConv;
}
}

var cxxMethodDecl = functionDecl as CXXMethodDecl;
Expand Down Expand Up @@ -740,6 +742,7 @@ void ForFunctionDecl(ParmVarDecl parmVarDecl, FunctionDecl functionDecl)
}

AddNativeTypeNameAttribute(nativeTypeName, prefix: "", postfix: " ");
AddCppAttributes(parmVarDecl, prefix: "", postfix: " ");

_outputBuilder.Write(typeName);
_outputBuilder.Write(' ');
Expand Down Expand Up @@ -775,6 +778,7 @@ void ForTypedefDecl(ParmVarDecl parmVarDecl, TypedefDecl typedefDecl)
var type = parmVarDecl.Type;
var typeName = GetRemappedTypeName(parmVarDecl, context: null, type, out var nativeTypeName);
AddNativeTypeNameAttribute(nativeTypeName, prefix: "", postfix: " ");
AddCppAttributes(parmVarDecl, prefix: "", postfix: " ");

_outputBuilder.Write(typeName);
_outputBuilder.Write(' ');
Expand Down Expand Up @@ -934,6 +938,7 @@ private void VisitRecordDecl(RecordDecl recordDecl)
}

AddNativeTypeNameAttribute(nativeTypeNameBuilder.ToString());
AddNativeInheritanceAttribute(GetCursorName(cxxRecordDecl.Bases.Last().Referenced));
}

_outputBuilder.WriteIndented(GetAccessSpecifierName(recordDecl));
Expand Down Expand Up @@ -1590,6 +1595,11 @@ void VisitAnonymousRecordDecl(RecordDecl recordDecl, RecordDecl nestedRecordDecl

void VisitAnonymousRecordDeclFields(RecordDecl rootRecordDecl, RecordDecl anonymousRecordDecl, string contextType, string contextName)
{
if (_config.ExcludeAnonymousFieldHelpers)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think this should be a general ExcludeHelpers or that there should be an Exclude*Helper per "helper type" (just to cover any future helper additions)

{
return;
}

foreach (var declaration in anonymousRecordDecl.Decls)
{
if (declaration is FieldDecl fieldDecl)
Expand Down Expand Up @@ -2489,9 +2499,26 @@ void ForUnderlyingType(TypedefDecl typedefDecl, Type underlyingType)
{
ForPointeeType(typedefDecl, parentType: null, referenceType.PointeeType);
}
else if (underlyingType is TagType)
else if (underlyingType is TagType underlyingTagType)
{
// Nothing to do for tag types
// See if there's a potential typedef remapping we want to log
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Just for consistency, I think this logic should be in a ForTagType local function. It can then early exit with if (!_config.LogPotentialTypedefRemappings)

if (_config.LogPotentialTypedefRemappings)
{
var typedefName = typedefDecl.UnderlyingDecl.Name;
var possibleNamesToRemap = new string[] { "_" + typedefName, "_tag" + typedefName, "tag" + typedefName };
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just looking at the Windows bindings I've done for my own project, what about $"{typedefName}_tag", $"{typedefName}_", and $"{typedefName}__"?

var underlyingName = underlyingTagType.AsString;

foreach (var possibleNameToRemap in possibleNamesToRemap)
{
if (!_config.RemappedNames.ContainsKey(possibleNameToRemap))
{
if (possibleNameToRemap == underlyingName)
{
AddDiagnostic(DiagnosticLevel.Info, $"Potential remap: {possibleNameToRemap}={typedefName}");
}
}
}
}
}
else if (underlyingType is TypedefType typedefType)
{
Expand Down
107 changes: 104 additions & 3 deletions sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,94 @@ private void AddDiagnostic(DiagnosticLevel level, string message, Cursor cursor)
_diagnostics.Add(diagnostic);
}

private void AddCppAttributes(ParmVarDecl parmVarDecl, string prefix = null, string postfix = null)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just want to confirm. This basically saves all the C++ attributes separated by ^ in a C# style attribute?

{
if (!_config.GenerateCppAttributes)
{
return;
}

if (parmVarDecl.Attrs.Count == 0)
{
return;
}

if (prefix is null)
{
_outputBuilder.WriteIndentation();
}
else
{
_outputBuilder.WriteNewlineIfNeeded();
_outputBuilder.Write(prefix);
}

_outputBuilder.Write($"[CppAttributeList(\"");

_outputBuilder.Write(EscapeString(parmVarDecl.Attrs[0].Spelling));
for (int i = 1; i < parmVarDecl.Attrs.Count; i++)
{
// Separator char between attributes
_outputBuilder.Write('^');

_outputBuilder.Write(EscapeString(parmVarDecl.Attrs[i].Spelling));
}

_outputBuilder.Write($"\")]");

if (postfix is null)
{
_outputBuilder.NeedsNewline = true;
}
else
{
_outputBuilder.Write(postfix);
}
}

private void AddNativeInheritanceAttribute(string inheritedFromName, string prefix = null, string postfix = null, string attributePrefix = null)
{
if (!_config.GenerateNativeInheritanceAttribute)
{
return;
}

if (prefix is null)
{
_outputBuilder.WriteIndentation();
}
else
{
_outputBuilder.WriteNewlineIfNeeded();
_outputBuilder.Write(prefix);
}

_outputBuilder.Write('[');

if (attributePrefix != null)
{
_outputBuilder.Write(attributePrefix);
}

_outputBuilder.Write("NativeInheritance");
_outputBuilder.Write('(');

_outputBuilder.Write('"');
_outputBuilder.Write(EscapeString(inheritedFromName));
_outputBuilder.Write('"');
_outputBuilder.Write(')');
_outputBuilder.Write(']');

if (postfix is null)
{
_outputBuilder.NeedsNewline = true;
}
else
{
_outputBuilder.Write(postfix);
}
}

private void AddNativeTypeNameAttribute(string nativeTypeName, string prefix = null, string postfix = null, string attributePrefix = null)
{
if (string.IsNullOrWhiteSpace(nativeTypeName))
Expand Down Expand Up @@ -991,10 +1079,10 @@ private string GetRemappedCursorName(NamedDecl namedDecl)
}
else if ((namedDecl is RecordDecl recordDecl) && name.StartsWith("__AnonymousRecord_"))
{
remappedName = "_Anonymous";

if (recordDecl.Parent is RecordDecl parentRecordDecl)
{
remappedName = "_Anonymous";

var matchingField = parentRecordDecl.Fields.Where((fieldDecl) => fieldDecl.Type.CanonicalType == recordDecl.TypeForDecl.CanonicalType).FirstOrDefault();

if (matchingField != null)
Expand All @@ -1007,8 +1095,9 @@ private string GetRemappedCursorName(NamedDecl namedDecl)
var index = parentRecordDecl.AnonymousDecls.IndexOf(recordDecl) + 1;
remappedName += index.ToString();
}

remappedName += $"_e__{(recordDecl.IsUnion ? "Union" : "Struct")}";
}
remappedName += $"_e__{(recordDecl.IsUnion ? "Union" : "Struct")}";
}

return remappedName;
Expand Down Expand Up @@ -1087,6 +1176,10 @@ private string GetRemappedTypeName(Cursor cursor, Cursor context, Type type, out

name += $"_e__{(recordDecl.IsUnion ? "Union" : "Struct")}";
}
else if ((canonicalType is EnumType enumType) && name.StartsWith("__AnonymousEnum_"))
{
name = GetRemappedTypeName(enumType.Decl, context: null, enumType.Decl.IntegerType, out _);
}
else if (cursor is EnumDecl enumDecl)
{
var enumDeclName = GetRemappedCursorName(enumDecl);
Expand Down Expand Up @@ -1906,6 +1999,14 @@ private bool IsExcluded(Cursor cursor, out bool isExcludedByConflictingDefinitio
return false;
}

if (_config.ExcludeFunctionsWithBody &&
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IsExcluded is actually one of the "hottest" functions in ClangSharp. It would probably be good to keep this after the IsExcludedByFile check which catches most of the exclusions.

cursor is FunctionDecl functionDecl &&
cursor.CursorKind == CXCursorKind.CXCursor_FunctionDecl &&
functionDecl.HasBody)
{
return true;
}

return IsExcludedByFile(cursor) || IsExcludedByName(cursor, out isExcludedByConflictingDefinition);

bool IsAlwaysIncluded(Cursor cursor)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,16 @@ public PInvokeGeneratorConfiguration(string libraryPath, string namespaceName, s

public bool LogVisitedFiles => _options.HasFlag(PInvokeGeneratorConfigurationOptions.LogVisitedFiles);

public bool ExcludeFunctionsWithBody => _options.HasFlag(PInvokeGeneratorConfigurationOptions.ExcludeFunctionsWithBody);

public bool ExcludeAnonymousFieldHelpers => _options.HasFlag(PInvokeGeneratorConfigurationOptions.ExcludeAnonymousFieldHelpers);

public bool LogPotentialTypedefRemappings => _options.HasFlag(PInvokeGeneratorConfigurationOptions.LogPotentialTypedefRemappings);

public bool GenerateCppAttributes => _options.HasFlag(PInvokeGeneratorConfigurationOptions.GenerateCppAttributes);

public bool GenerateNativeInheritanceAttribute => _options.HasFlag(PInvokeGeneratorConfigurationOptions.GenerateNativeInheritanceAttribute);

public string MethodClassName { get; }

public string MethodPrefixToStrip { get;}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,5 +42,15 @@ public enum PInvokeGeneratorConfigurationOptions
ExcludeEnumOperators = 0x00004000,

GenerateAggressiveInlining = 0x00008000,

ExcludeFunctionsWithBody = 0x00010000,

ExcludeAnonymousFieldHelpers = 0x00020000,

LogPotentialTypedefRemappings = 0x00040000,

GenerateCppAttributes = 0x00080000,

GenerateNativeInheritanceAttribute = 0x00100000,
}
}
30 changes: 30 additions & 0 deletions sources/ClangSharpPInvokeGenerator/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,36 @@ public static int Run(InvocationContext context)
break;
}

case "exclude-funcs-with-body":
{
configOptions |= PInvokeGeneratorConfigurationOptions.ExcludeFunctionsWithBody;
break;
}

case "log-potential-typedef-remappings":
{
configOptions |= PInvokeGeneratorConfigurationOptions.LogPotentialTypedefRemappings;
break;
}

case "exclude-anonymous-field-helpers":
{
configOptions |= PInvokeGeneratorConfigurationOptions.ExcludeAnonymousFieldHelpers;
break;
}

case "generate-cpp-attributes":
{
configOptions |= PInvokeGeneratorConfigurationOptions.GenerateCppAttributes;
break;
}

case "generate-native-inheritance-attribute":
{
configOptions |= PInvokeGeneratorConfigurationOptions.GenerateNativeInheritanceAttribute;
break;
}

default:
{
errorList.Add($"Error: Unrecognized config switch: {configSwitch}.");
Expand Down