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
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,7 @@ Options:
-v, --version Prints the current version information for the tool and its native dependencies.
-was, --with-access-specifier <with-access-specifier> An access specifier to be used with the given qualified or remapped declaration name during binding generation. Supports `*` (any run) and `?` (single character) wildcards; exact matches take precedence. []
-wa, --with-attribute <with-attribute> An attribute to be added to the given remapped declaration name during binding generation. Supports `*` (any run) and `?` (single character) wildcards; exact matches take precedence. []
-wb, --with-base <with-base> An additional base type the generated type should derive from during binding generation. Applies to structs and COM interface types. Supports `*` (any run) and `?` (single character) wildcards; exact matches take precedence. []
-wcc, --with-callconv <with-callconv> A calling convention to be used for the given declaration during binding generation. Supports `*` (any run) and `?` (single character) wildcards; exact matches take precedence. []
-wc, --with-class <with-class> A class to be used for the given remapped constant or function declaration name during binding generation. Supports a trailing `*` wildcard for prefix matching; an exact match takes precedence. []
-wcond, --with-conditional <symbol> A preprocessor symbol used to wrap single-file C# output in a leading '#if <symbol>' and trailing '#endif'. Useful when files can't be conditionally excluded at the project level (e.g. Unity). []
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd">
<metadata minClientVersion="2.12">
<id>libClangSharp.runtime.linux-arm64</id>
<version>22.1.8.2</version>
<version>22.1.8.3</version>
<authors>.NET Foundation and Contributors</authors>
<owners>.NET Foundation and Contributors</owners>
<requireLicenseAcceptance>true</requireLicenseAcceptance>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd">
<metadata minClientVersion="2.12">
<id>libClangSharp.runtime.linux-x64</id>
<version>22.1.8.2</version>
<version>22.1.8.3</version>
<authors>.NET Foundation and Contributors</authors>
<owners>.NET Foundation and Contributors</owners>
<requireLicenseAcceptance>true</requireLicenseAcceptance>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd">
<metadata minClientVersion="2.12">
<id>libClangSharp.runtime.osx-arm64</id>
<version>22.1.8.2</version>
<version>22.1.8.3</version>
<authors>.NET Foundation and Contributors</authors>
<owners>.NET Foundation and Contributors</owners>
<requireLicenseAcceptance>true</requireLicenseAcceptance>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd">
<metadata minClientVersion="2.12">
<id>libClangSharp.runtime.win-arm64</id>
<version>22.1.8.2</version>
<version>22.1.8.3</version>
<authors>.NET Foundation and Contributors</authors>
<owners>.NET Foundation and Contributors</owners>
<requireLicenseAcceptance>true</requireLicenseAcceptance>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd">
<metadata minClientVersion="2.12">
<id>libClangSharp.runtime.win-x64</id>
<version>22.1.8.2</version>
<version>22.1.8.3</version>
<authors>.NET Foundation and Contributors</authors>
<owners>.NET Foundation and Contributors</owners>
<requireLicenseAcceptance>true</requireLicenseAcceptance>
Expand Down
2 changes: 1 addition & 1 deletion packages/libClangSharp/libClangSharp/libClangSharp.nuspec
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd">
<metadata minClientVersion="2.12">
<id>libClangSharp</id>
<version>22.1.8.2</version>
<version>22.1.8.3</version>
<authors>.NET Foundation and Contributors</authors>
<owners>.NET Foundation and Contributors</owners>
<requireLicenseAcceptance>true</requireLicenseAcceptance>
Expand Down
10 changes: 5 additions & 5 deletions packages/libClangSharp/libClangSharp/runtime.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,27 @@
"runtimes": {
"linux-arm64": {
"libClangSharp": {
"libClangSharp.runtime.linux-arm64": "22.1.8.2"
"libClangSharp.runtime.linux-arm64": "22.1.8.3"
}
},
"linux-x64": {
"libClangSharp": {
"libClangSharp.runtime.linux-x64": "22.1.8.2"
"libClangSharp.runtime.linux-x64": "22.1.8.3"
}
},
"osx-arm64": {
"libClangSharp": {
"libClangSharp.runtime.osx-arm64": "22.1.8.2"
"libClangSharp.runtime.osx-arm64": "22.1.8.3"
}
},
"win-arm64": {
"libClangSharp": {
"libClangSharp.runtime.win-arm64": "22.1.8.2"
"libClangSharp.runtime.win-arm64": "22.1.8.3"
}
},
"win-x64": {
"libClangSharp": {
"libClangSharp.runtime.win-x64": "22.1.8.2"
"libClangSharp.runtime.win-x64": "22.1.8.3"
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Collections.Generic;
using ClangSharp.CSharp;

namespace ClangSharp.Abstractions;
Expand Down Expand Up @@ -49,7 +50,7 @@ internal partial interface IOutputBuilder
void EndFunctionOrDelegate(in FunctionOrDelegateDesc info);

void BeginStruct(in StructDesc info);
void BeginMarkerInterface(string[]? baseTypeNames);
void BeginMarkerInterface(string[]? baseTypeNames, IReadOnlyList<string>? extraBaseTypeNames);
void EndMarkerInterface();
void BeginExplicitVtbl();
void EndExplicitVtbl();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) .NET Foundation and Contributors. All Rights Reserved. Licensed under the MIT License (MIT). See License.md in the repository root for more information.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using ClangSharp.Interop;
Expand All @@ -17,6 +18,7 @@ internal struct StructDesc
public LayoutDesc Layout { get; set; }
public Guid? Uuid { get; set; }
public StructFlags Flags { get; set; }
public IReadOnlyList<string>? ExtraBaseTypeNames { get; set; }
public CXSourceLocation? Location { get; set; }

public bool IsNested
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -854,6 +854,11 @@ public void BeginStruct(in StructDesc desc)
baseTypeNames.Add($"IEquatable<{desc.EscapedName}>");
}

if (desc.ExtraBaseTypeNames is not null)
{
baseTypeNames.AddRange(desc.ExtraBaseTypeNames);
}

if (baseTypeNames.Count != 0)
{
Write(" : ");
Expand All @@ -864,24 +869,31 @@ public void BeginStruct(in StructDesc desc)
WriteBlockStart();
}

public void BeginMarkerInterface(string[]? baseTypeNames)
public void BeginMarkerInterface(string[]? baseTypeNames, IReadOnlyList<string>? extraBaseTypeNames)
{
WriteIndented("public interface Interface");

var bases = new List<string>();

if (baseTypeNames is not null)
{
Write(" : ");
Write(baseTypeNames[0]);
Write(".Interface");

for (var i = 1; i < baseTypeNames.Length; i++)
foreach (var baseTypeName in baseTypeNames)
{
Write(", ");
Write(baseTypeNames[i]);
Write(".Interface");
bases.Add($"{baseTypeName}.Interface");
}
}

if (extraBaseTypeNames is not null)
{
bases.AddRange(extraBaseTypeNames);
}

if (bases.Count != 0)
{
Write(" : ");
Write(string.Join(", ", bases));
}

WriteNewline();
WriteBlockStart();
_isInMarkerInterface = true;
Expand Down
24 changes: 18 additions & 6 deletions sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.Naming.cs
Original file line number Diff line number Diff line change
Expand Up @@ -202,23 +202,21 @@ private static string GetNamespaceQualifiedNativeTypeName(Decl decl, string nati
// Rebuild the dropped `Namespace::` prefix from the decl so the NativeTypeName keeps the
// fully qualified source spelling (e.g. `Gdiplus::Status`) and stays stable across versions.

var qualifierBuilder = new StringBuilder();
var namespaceNames = new List<string>();

for (var declContext = decl.DeclContext; declContext is Decl parentDecl; declContext = parentDecl.DeclContext)
{
if (parentDecl is NamespaceDecl namespaceDecl && !string.IsNullOrEmpty(namespaceDecl.Name))
{
_ = qualifierBuilder.Insert(0, "::").Insert(0, namespaceDecl.Name);
namespaceNames.Insert(0, namespaceDecl.Name);
}
}

if (qualifierBuilder.Length == 0)
if (namespaceNames.Count == 0)
{
return nativeTypeName;
}

var qualifier = qualifierBuilder.ToString();

// The qualifier belongs on the type name, after any leading cv-qualifiers, elaborated tag
// keywords, or `__unaligned`, so e.g. a `const struct` pointer stays `const struct Ns::Point *`
// rather than the malformed `Ns::const struct Point *`.
Expand All @@ -245,7 +243,21 @@ private static string GetNamespaceQualifiedNativeTypeName(Decl decl, string nati
}
}

return nativeTypeName.AsSpan(offset).StartsWith(qualifier, StringComparison.Ordinal) ? nativeTypeName : nativeTypeName.Insert(offset, qualifier);
// The source spelling may already carry a trailing run of the namespace (a minimally
// qualified reference, e.g. `Windows::Foundation::IPropertyValue` written inside
// `namespace ABI`). Insert only the leading segments it is missing so the qualifier isn't
// duplicated into `ABI::Windows::Foundation::Windows::Foundation::IPropertyValue`.
for (var index = 0; index <= namespaceNames.Count; index++)
{
var suffix = index < namespaceNames.Count ? string.Concat(string.Join("::", namespaceNames.Skip(index)), "::") : "";

if (nativeTypeName.AsSpan(offset).StartsWith(suffix, StringComparison.Ordinal))
{
return index == 0 ? nativeTypeName : nativeTypeName.Insert(offset, string.Concat(string.Join("::", namespaceNames.Take(index)), "::"));
}
}

return nativeTypeName;
}

private string GetCursorQualifiedName(NamedDecl namedDecl, bool truncateParameters = false)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,11 @@ private void VisitRecordDecl(RecordDecl recordDecl)

string[]? baseTypeNames = null;

if (!TryGetRemappedValue(recordDecl, _config._withBases, optOuts: null, out var extraBaseTypeNames, matchStar: true))
{
extraBaseTypeNames = null;
}

string? nativeNameWithExtras = null, nativeInheritance = null;
if ((cxxRecordDecl is not null) && cxxRecordDecl.Bases.Any())
{
Expand Down Expand Up @@ -231,6 +236,7 @@ private void VisitRecordDecl(RecordDecl recordDecl)
},
Uuid = nullableUuid,
NativeType = nativeNameWithExtras,
ExtraBaseTypeNames = ((hasVtbl || hasBaseVtbl) && _config.GenerateMarkerInterfaces) ? null : extraBaseTypeNames,
NativeInheritance = _config.GenerateNativeInheritanceAttribute ? nativeInheritance : null,
NativeAlignment = nativeAlignment,
Location = recordDecl.Location,
Expand Down Expand Up @@ -625,7 +631,7 @@ private void VisitRecordDecl(RecordDecl recordDecl)
csharpOutputBuilder.NeedsNewline = true;
}

_outputBuilder.BeginMarkerInterface(baseTypeNames);
_outputBuilder.BeginMarkerInterface(baseTypeNames, extraBaseTypeNames);
OutputMarkerInterfaces(cxxRecordDecl, cxxRecordDecl);
_outputBuilder.EndMarkerInterface();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -850,6 +850,18 @@ private void VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr cxxFunctionalCastE
}
}

private void VisitCXXDeleteExpr(CXXDeleteExpr cxxDeleteExpr)
{
// Mirrors the `cxx_new<T>(...)` placeholder emitted for `new`: `delete`/`delete[]` has no
// direct C# equivalent (the allocator is unknown), so emit a visible `cxx_delete(...)` call
// rather than nothing, which would leave a silently empty statement such as `if (p) { ; }`.
var outputBuilder = StartCSharpCode();
outputBuilder.Write("cxx_delete(");
Visit(cxxDeleteExpr.Argument);
outputBuilder.Write(')');
StopCSharpCode();
}

private void VisitCXXNewExpr(CXXNewExpr cxxNewExpr)
{
var outputBuilder = StartCSharpCode();
Expand Down Expand Up @@ -2645,7 +2657,12 @@ private void VisitStmt(Stmt stmt)
}

// case CX_StmtClass_CXXDefaultInitExpr:
// case CX_StmtClass_CXXDeleteExpr:

case CX_StmtClass_CXXDeleteExpr:
{
VisitCXXDeleteExpr((CXXDeleteExpr)stmt);
break;
}

case CX_StmtClass_CXXDependentScopeMemberExpr:
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ public sealed class PInvokeGeneratorConfiguration
internal readonly Dictionary<string, string> _remappedFieldNames;
internal readonly Dictionary<string, AccessSpecifier> _withAccessSpecifiers;
internal readonly Dictionary<string, IReadOnlyList<string>> _withAttributes;
internal readonly Dictionary<string, IReadOnlyList<string>> _withBases;
internal readonly Dictionary<string, string> _withCallConvs;
internal readonly Dictionary<string, string> _withClasses;
internal readonly Dictionary<string, string> _withEnumMemberStrip;
Expand Down Expand Up @@ -137,6 +138,7 @@ public PInvokeGeneratorConfiguration(string language, string languageStandard, s
_remappedFieldNames = new Dictionary<string, string>(QualifiedNameComparer.Default);
_withAccessSpecifiers = new Dictionary<string, AccessSpecifier>(QualifiedNameComparer.Default);
_withAttributes = new Dictionary<string, IReadOnlyList<string>>(QualifiedNameComparer.Default);
_withBases = new Dictionary<string, IReadOnlyList<string>>(QualifiedNameComparer.Default);
_withCallConvs = new Dictionary<string, string>(QualifiedNameComparer.Default);
_withClasses = new Dictionary<string, string>(StringComparer.Ordinal);
_withEnumMemberStrip = new Dictionary<string, string>(QualifiedNameComparer.Default);
Expand Down Expand Up @@ -609,6 +611,20 @@ public IReadOnlyDictionary<string, IReadOnlyList<string>> WithAttributes
}
}

[AllowNull]
public IReadOnlyDictionary<string, IReadOnlyList<string>> WithBases
{
get
{
return _withBases;
}

init
{
AddRange(_withBases, value);
}
}

[AllowNull]
public IReadOnlyDictionary<string, string> WithCallConvs
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) .NET Foundation and Contributors. All Rights Reserved. Licensed under the MIT License (MIT). See License.md in the repository root for more information.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Text;
Expand Down Expand Up @@ -308,7 +309,7 @@ public void BeginStruct(in StructDesc info)
_ = _sb.Append('>');
info.WriteCustomAttrs?.Invoke(info.CustomAttrGeneratorData);
}
public void BeginMarkerInterface(string[]? baseTypeNames) => _sb.Append("<interface>");
public void BeginMarkerInterface(string[]? baseTypeNames, IReadOnlyList<string>? extraBaseTypeNames) => _sb.Append("<interface>");
public void EndMarkerInterface() => _sb.Append("</interface>");
public void BeginExplicitVtbl() => _sb.Append("<vtbl>");
public void EndExplicitVtbl() => _sb.Append("</vtbl>");
Expand Down
3 changes: 3 additions & 0 deletions sources/ClangSharpPInvokeGenerator/Program.Options.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ internal static partial class Program
private static readonly string[] s_versionOptionAliases = ["--version", "-v"];
private static readonly string[] s_withAccessSpecifierOptionAliases = ["--with-access-specifier", "-was"];
private static readonly string[] s_withAttributeOptionAliases = ["--with-attribute", "-wa"];
private static readonly string[] s_withBaseOptionAliases = ["--with-base", "-wb"];
private static readonly string[] s_withCallConvOptionAliases = ["--with-callconv", "-wcc"];
private static readonly string[] s_withClassOptionAliases = ["--with-class", "-wc"];
private static readonly string[] s_withConditionalOptionAliases = ["--with-conditional", "-wcond"];
Expand Down Expand Up @@ -98,6 +99,7 @@ internal static partial class Program
private static readonly CommandLineOption s_versionOption = Flag(s_versionOptionAliases, "Prints the current version information for the tool and its native dependencies.");
private static readonly CommandLineOption s_withAccessSpecifierNameValuePairs = Multi(s_withAccessSpecifierOptionAliases, "An access specifier to be used with the given qualified or remapped declaration name during binding generation. Supports `*` (any run) and `?` (single character) wildcards; exact matches take precedence.");
private static readonly CommandLineOption s_withAttributeNameValuePairs = Multi(s_withAttributeOptionAliases, "An attribute to be added to the given remapped declaration name during binding generation. Supports `*` (any run) and `?` (single character) wildcards; exact matches take precedence.");
private static readonly CommandLineOption s_withBaseNameValuePairs = Multi(s_withBaseOptionAliases, "An additional base type the generated type should derive from during binding generation. Applies to structs and COM interface types. Supports `*` (any run) and `?` (single character) wildcards; exact matches take precedence.");
private static readonly CommandLineOption s_withCallConvNameValuePairs = Multi(s_withCallConvOptionAliases, "A calling convention to be used for the given declaration during binding generation. Supports `*` (any run) and `?` (single character) wildcards; exact matches take precedence.");
private static readonly CommandLineOption s_withClassNameValuePairs = Multi(s_withClassOptionAliases, "A class to be used for the given remapped constant or function declaration name during binding generation. Supports a trailing `*` wildcard for prefix matching; an exact match takes precedence.");
private static readonly CommandLineOption s_withConditional = Single(s_withConditionalOptionAliases, "A preprocessor symbol used to wrap single-file C# output in a leading '#if <symbol>' and trailing '#endif'. Useful when files can't be conditionally excluded at the project level (e.g. Unity).", valueName: "symbol");
Expand Down Expand Up @@ -163,6 +165,7 @@ internal static partial class Program
s_versionOption,
s_withAccessSpecifierNameValuePairs,
s_withAttributeNameValuePairs,
s_withBaseNameValuePairs,
s_withCallConvNameValuePairs,
s_withClassNameValuePairs,
s_withConditional,
Expand Down
Loading
Loading