Skip to content

Add regex support to remapping and attributes #615

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

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -2722,7 +2722,7 @@ void VisitBitfieldDecl(FieldDecl fieldDecl, BitfieldDesc[] bitfieldDescs, Record
var recordDeclName = GetCursorName(recordDecl);

var isSmallType = currentSize < 4;
var isRemappedToSelf = _config.RemappedNames.TryGetValue(typeName, out var remappedTypeName) && typeName.Equals(remappedTypeName, StringComparison.Ordinal);
var isRemappedToSelf = (_config.RemappedNames.TryGetValue(typeName, out var remappedTypeName) || TryRemapRegex(typeName,out remappedTypeName)) && typeName.Equals(remappedTypeName, StringComparison.Ordinal);
var isTypeMismatch = type != builtinTypeBacking;
var isUnsignedToSigned = !isTypeBackingSigned && isTypeSigned;

Expand Down
154 changes: 134 additions & 20 deletions sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public sealed partial class PInvokeGenerator : IDisposable
private readonly Dictionary<string, HashSet<string>> _topLevelClassUsings;
private readonly Dictionary<string, List<string>> _topLevelClassAttributes;
private readonly HashSet<string> _topLevelClassNames;
private readonly HashSet<string> _usedRemappings;
private readonly SortedDictionary<string,string> _usedRemappings;
private readonly string _placeholderMacroType;

private string _filePath;
Expand Down Expand Up @@ -1747,7 +1747,7 @@ public void GenerateBindings(TranslationUnit translationUnit, string filePath, s
}
}

if (!_config.RemappedNames.TryGetValue(name, out _))
if (!(_config.RemappedNames.TryGetValue(name, out _) || TryRemapRegex(name,out _)))
{
var addDiag = false;

Expand All @@ -1769,9 +1769,9 @@ public void GenerateBindings(TranslationUnit translationUnit, string filePath, s
addDiag = true;
}

if (!addDiag && !_config.RemappedNames.TryGetValue(altName, out _))
if (!addDiag && !(_config.RemappedNames.TryGetValue(altName, out _) || TryRemapRegex(altName,out _)))
{
if (!_config.RemappedNames.TryGetValue(smlName, out _))
if (!(_config.RemappedNames.TryGetValue(smlName, out _) || TryRemapRegex(smlName,out _)))
{
addDiag = true;
}
Expand All @@ -1789,7 +1789,7 @@ public void GenerateBindings(TranslationUnit translationUnit, string filePath, s
var name = kvp.Key;
var remappings = kvp.Value;

if (_config.RemappedNames.TryGetValue(name, out var remappedName) && !remappings.Contains(remappedName) && (name != remappedName) && !_config.ForceRemappedNames.Contains(name))
if ((_config.RemappedNames.TryGetValue(name, out var remappedName) || TryRemapRegex(name, out remappedName)) && !remappings.Contains(remappedName) && (name != remappedName) && !(_config.ForceRemappedNames.Contains(name) || _config.ForceRemappedRegexes.Any(c => c.IsMatch(name))))
{
var addDiag = false;

Expand All @@ -1811,9 +1811,9 @@ public void GenerateBindings(TranslationUnit translationUnit, string filePath, s
addDiag = true;
}

if (!addDiag && _config.RemappedNames.TryGetValue(altName, out remappedName) && !remappings.Contains(remappedName) && (altName != remappedName) && !_config.ForceRemappedNames.Contains(altName))
if (!addDiag && (_config.RemappedNames.TryGetValue(altName, out remappedName) || TryRemapRegex(altName,out remappedName)) && !remappings.Contains(remappedName) && (altName != remappedName) && !(_config.ForceRemappedNames.Contains(altName) || _config.ForceRemappedRegexes.Any(c => c.IsMatch(altName))))
{
if (_config.RemappedNames.TryGetValue(smlName, out remappedName) && !remappings.Contains(remappedName) && (smlName != remappedName) && !_config.ForceRemappedNames.Contains(smlName))
if ((_config.RemappedNames.TryGetValue(smlName, out remappedName) || TryRemapRegex(smlName,out remappedName)) && !remappings.Contains(remappedName) && (smlName != remappedName) && !(_config.ForceRemappedNames.Contains(smlName) || _config.ForceRemappedRegexes.Any(c => c.IsMatch(smlName))))
{
addDiag = true;
}
Expand All @@ -1826,11 +1826,9 @@ public void GenerateBindings(TranslationUnit translationUnit, string filePath, s
}
}

foreach (var name in _usedRemappings)
foreach (var (name,remappedName) in _usedRemappings)
{
var remappedName = _config.RemappedNames[name];

if (!_allValidNameRemappings.ContainsKey(name) && (name != remappedName) && !_config.ForceRemappedNames.Contains(name))
if (!_allValidNameRemappings.ContainsKey(name) && (name != remappedName) && !(_config.ForceRemappedNames.Contains(name) || _config.ForceRemappedRegexes.Any(c => c.IsMatch(name))))
{
var addDiag = false;

Expand All @@ -1852,9 +1850,9 @@ public void GenerateBindings(TranslationUnit translationUnit, string filePath, s
addDiag = true;
}

if (!addDiag && !_allValidNameRemappings.ContainsKey(altName) && (altName != remappedName) && !_config.ForceRemappedNames.Contains(altName))
if (!addDiag && !_allValidNameRemappings.ContainsKey(altName) && (altName != remappedName) && !(_config.ForceRemappedNames.Contains(altName) || _config.ForceRemappedRegexes.Any(c => c.IsMatch(altName))))
{
if (!_allValidNameRemappings.ContainsKey(smlName) && (smlName != remappedName) && !_config.ForceRemappedNames.Contains(smlName))
if (!_allValidNameRemappings.ContainsKey(smlName) && (smlName != remappedName) && !(_config.ForceRemappedNames.Contains(smlName) || _config.ForceRemappedRegexes.Any(c => c.IsMatch(smlName))))
{
addDiag = true;
}
Expand Down Expand Up @@ -3288,24 +3286,42 @@ private string GetRemappedNameForAnonymousRecord(RecordDecl recordDecl)
private string GetRemappedName(string name, Cursor? cursor, bool tryRemapOperatorName, out bool wasRemapped, bool skipUsing = false)
=> GetRemappedName(name, cursor, tryRemapOperatorName, out wasRemapped, skipUsing, skipUsingIfNotRemapped: skipUsing);

private bool TryRemapRegex(string name,[MaybeNullWhen(false)] out string remappedName)
{
foreach (var remapRegex in _config.RemappedRegexes.Keys)
{
var match = remapRegex.Match(name);
if(!match.Success)
{
continue;
}

var groups = match.Groups.Values.Skip(1).Select(object (group) => group.Value).ToArray();
remappedName = string.Format(CultureInfo.InvariantCulture,_config.RemappedRegexes[remapRegex],groups);
return true;
}

remappedName = null;
return false;
}
private string GetRemappedName(string name, Cursor? cursor, bool tryRemapOperatorName, out bool wasRemapped, bool skipUsing, bool skipUsingIfNotRemapped)
{
if (_config.RemappedNames.TryGetValue(name, out var remappedName))
if (_config.RemappedNames.TryGetValue(name, out var remappedName) || TryRemapRegex(name, out remappedName))
{
wasRemapped = true;
_ = _usedRemappings.Add(name);
_ = _usedRemappings.TryAdd(name,remappedName);
return AddUsingDirectiveIfNeeded(_outputBuilder, remappedName, skipUsing);
}

if (name.StartsWith("const ", StringComparison.Ordinal))
{
var tmpName = name[6..];

if (_config.RemappedNames.TryGetValue(tmpName, out remappedName))
if (_config.RemappedNames.TryGetValue(tmpName, out remappedName) || TryRemapRegex(tmpName, out remappedName))
{

wasRemapped = true;
_ = _usedRemappings.Add(tmpName);
_ = _usedRemappings.TryAdd(tmpName,remappedName);
return AddUsingDirectiveIfNeeded(_outputBuilder, remappedName, skipUsing);
}
}
Expand Down Expand Up @@ -6807,11 +6823,23 @@ private void WithAttributes(NamedDecl namedDecl, bool onlySupportedOSPlatform =
var outputBuilder = isTestOutput ? _testOutputBuilder : _outputBuilder;
Debug.Assert(outputBuilder is not null);

if (TryGetRemappedValue(namedDecl, _config.WithAttributes, out var attributes, matchStar: true))
{
foreach (var attribute in attributes.Where((a) => !onlySupportedOSPlatform || a.StartsWith("SupportedOSPlatform(", StringComparison.Ordinal)))
if (TryGetRemappedValue(namedDecl, _config.WithAttributes, out var attributes, matchStar: true))
{
outputBuilder.WriteCustomAttribute(attribute);
foreach (var attribute in attributes.Where((a) => !onlySupportedOSPlatform || a.StartsWith("SupportedOSPlatform(", StringComparison.Ordinal)))
{
outputBuilder.WriteCustomAttribute(attribute);
}
}
}

{
if (TryGetRemappedValueRegex(namedDecl, _config.WithAttributesRegex, out var attributes))
{
foreach (var attribute in attributes.Where((a) => !onlySupportedOSPlatform || a.StartsWith("SupportedOSPlatform(", StringComparison.Ordinal)))
{
outputBuilder.WriteCustomAttribute(attribute);
}
}
}

Expand Down Expand Up @@ -7090,6 +7118,92 @@ private bool TryGetRemappedValue<T>(NamedDecl namedDecl, IReadOnlyDictionary<str
return false;
}

private bool TryGetRemappedValueRegex<T>(NamedDecl namedDecl, IReadOnlyDictionary<Regex, T> remappings, [MaybeNullWhen(false)] out T value)
{
var name = GetCursorQualifiedName(namedDecl);

if (name.StartsWith("ClangSharpMacro_", StringComparison.Ordinal))
{
name = name["ClangSharpMacro_".Length..];
}

foreach (var regex in remappings.Keys)
{
if (!regex.IsMatch(name))
{
continue;
}

value = remappings[regex];
return true;
}

name = name.Replace("::", ".", StringComparison.Ordinal);

foreach (var regex in remappings.Keys)
{
if (!regex.IsMatch(name))
{
continue;
}

value = remappings[regex];
return true;
}

name = GetCursorQualifiedName(namedDecl, truncateParameters: true);

if (name.StartsWith("ClangSharpMacro_", StringComparison.Ordinal))
{
name = name["ClangSharpMacro_".Length..];
}

foreach (var regex in remappings.Keys)
{
if (!regex.IsMatch(name))
{
continue;
}

value = remappings[regex];
return true;
}

name = name.Replace("::", ".", StringComparison.Ordinal);

foreach (var regex in remappings.Keys)
{
if (!regex.IsMatch(name))
{
continue;
}

value = remappings[regex];
return true;
}

name = GetRemappedCursorName(namedDecl);

if (name.StartsWith("ClangSharpMacro_", StringComparison.Ordinal))
{
name = name["ClangSharpMacro_".Length..];
}

foreach (var regex in remappings.Keys)
{
if (!regex.IsMatch(name))
{
continue;
}

value = remappings[regex];
return true;
}

value = default;
return false;
}

private void WithTestAttribute()
{
if (_config.GenerateTestsNUnit)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Text.RegularExpressions;

namespace ClangSharp;

Expand All @@ -29,6 +30,7 @@ public sealed class PInvokeGeneratorConfiguration

private readonly SortedSet<string> _excludedNames;
private readonly SortedSet<string> _forceRemappedNames;
private readonly SortedSet<Regex> _forceRemappedRegexes;
private readonly SortedSet<string> _includedNames;
private readonly SortedSet<string> _nativeTypeNamesToStrip;
private readonly SortedSet<string> _withManualImports;
Expand All @@ -37,8 +39,10 @@ public sealed class PInvokeGeneratorConfiguration
private readonly SortedSet<string> _withSuppressGCTransitions;

private readonly SortedDictionary<string, string> _remappedNames;
private readonly SortedDictionary<Regex, string> _remappedRegexes;
private readonly SortedDictionary<string, AccessSpecifier> _withAccessSpecifiers;
private readonly SortedDictionary<string, IReadOnlyList<string>> _withAttributes;
private readonly SortedDictionary<Regex, IReadOnlyList<string>> _withAttributesRegex;
private readonly SortedDictionary<string, string> _withCallConvs;
private readonly SortedDictionary<string, string> _withClasses;
private readonly SortedDictionary<string, Guid> _withGuids;
Expand Down Expand Up @@ -83,6 +87,7 @@ public PInvokeGeneratorConfiguration(string language, string languageStandard, s

_excludedNames = [];
_forceRemappedNames = [];
_forceRemappedRegexes = new SortedSet<Regex>(new RegexComparer());
_includedNames = [];
_nativeTypeNamesToStrip = [];
_withManualImports = [];
Expand All @@ -91,8 +96,10 @@ public PInvokeGeneratorConfiguration(string language, string languageStandard, s
_withSuppressGCTransitions = [];

_remappedNames = [];
_remappedRegexes = new SortedDictionary<Regex, string>(new RegexComparer());
_withAccessSpecifiers = [];
_withAttributes = [];
_withAttributesRegex = new SortedDictionary<Regex, IReadOnlyList<string>>(new RegexComparer());
_withCallConvs = [];
_withClasses = [];
_withGuids = [];
Expand Down Expand Up @@ -359,7 +366,23 @@ public IReadOnlyDictionary<string, string> RemappedNames
}
}

[AllowNull]
public IReadOnlyDictionary<Regex, string> RemappedRegexes
{
get
{
return _remappedRegexes;
}

init
{
AddRange(_forceRemappedRegexes, value,ValueStartsWithAt);
AddRange(_remappedRegexes, value,RemoveAtPrefix);
}
}

public IReadOnlyCollection<string> ForceRemappedNames => _forceRemappedNames;
public IReadOnlyCollection<Regex> ForceRemappedRegexes => _forceRemappedRegexes;

[AllowNull]
public string TestOutputLocation
Expand Down Expand Up @@ -418,6 +441,20 @@ public IReadOnlyDictionary<string, IReadOnlyList<string>> WithAttributes
}
}

[AllowNull]
public IReadOnlyDictionary<Regex, IReadOnlyList<string>> WithAttributesRegex
{
get
{
return _withAttributesRegex;
}

init
{
AddRange(_withAttributesRegex, value);
}
}

[AllowNull]
public IReadOnlyDictionary<string, string> WithCallConvs
{
Expand Down Expand Up @@ -612,7 +649,7 @@ public static AccessSpecifier ConvertStringToAccessSpecifier(string input)
: input.Equals("public", StringComparison.OrdinalIgnoreCase) ? AccessSpecifier.Public : AccessSpecifier.None;
}

private static void AddRange<TValue>(SortedDictionary<string, TValue> dictionary, IEnumerable<KeyValuePair<string, TValue>>? keyValuePairs)
private static void AddRange<TValue,TKey>(SortedDictionary<TKey, TValue> dictionary, IEnumerable<KeyValuePair<TKey, TValue>>? keyValuePairs) where TKey : notnull
{
if (keyValuePairs != null)
{
Expand All @@ -638,6 +675,19 @@ private static void AddRange<TInput, TValue>(SortedDictionary<string, TValue> di
}
}

private static void AddRange<TInput, TValue>(SortedDictionary<Regex, TValue> dictionary, IEnumerable<KeyValuePair<Regex, TInput>>? keyValuePairs, Func<TInput, TValue> convert)
{
if (keyValuePairs != null)
{
foreach (var keyValuePair in keyValuePairs)
{
// Use the indexer, rather than Add, so that any
// default mappings can be overwritten if desired.
dictionary[keyValuePair.Key] = convert(keyValuePair.Value);
}
}
}

private static void AddRange<TInput>(SortedSet<TInput> hashSet, IEnumerable<TInput>? keys)
{
if (keys != null)
Expand Down Expand Up @@ -674,6 +724,20 @@ private static void AddRange<TInput>(SortedSet<string> hashSet, IEnumerable<KeyV
}
}

private static void AddRange<TInput>(SortedSet<Regex> hashSet, IEnumerable<KeyValuePair<Regex, TInput>>? keyValuePairs, Func<TInput, bool> shouldAdd)
{
if (keyValuePairs != null)
{
foreach (var keyValuePair in keyValuePairs)
{
if (shouldAdd(keyValuePair.Value))
{
_ = hashSet.Add(keyValuePair.Key);
}
}
}
}

private static string RemoveAtPrefix(string value) => ValueStartsWithAt(value) ? value[1..] : value;

private static (string, PInvokeGeneratorTransparentStructKind) RemoveAtPrefix((string Name, PInvokeGeneratorTransparentStructKind Kind) value) => (ValueStartsWithAt(value.Name) ? value.Name[1..] : value.Name, value.Kind);
Expand Down
Loading