From 72d2c59f3c52b3e71b8baaceaf6911c1bc6f7712 Mon Sep 17 00:00:00 2001 From: Friendly Chicken <72442889+FriendlyChicken@users.noreply.github.com> Date: Sat, 2 Oct 2021 15:49:51 -0700 Subject: [PATCH] Implement configurable access modifiers for top-level types --- .../PInvokeGenerator.cs | 86 +++++++++++++++++++ .../PInvokeGeneratorConfiguration.cs | 7 +- sources/ClangSharpPInvokeGenerator/Program.cs | 18 +++- 3 files changed, 109 insertions(+), 2 deletions(-) diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs index 87c491b9..3e172eb9 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs @@ -714,9 +714,95 @@ internal static string EscapeString(string value) => value.Replace("\\", "\\\\") .Replace("\t", "\\t") .Replace("\"", "\\\""); + private bool GetConfigAccessSpecifier(NamedDecl namedDecl, out AccessSpecifier name) + { + name = AccessSpecifier.Public; + string access; + + // Top level declarations only + if(namedDecl is FieldDecl) + { + return false; + } + + if(namedDecl is FunctionDecl) + { + if (TryGetValue($"{_config.Namespace}.{_config.MethodClassName}.{namedDecl.Name}", out access) || TryGetValue($"{_config.MethodClassName}.{namedDecl.Name}", out access)) + { + return GetAccess(access, out name, namedDecl); + } + } + + if (TryGetValue($"{_config.Namespace}.{namedDecl.Name}", out access) || TryGetValue(namedDecl.Name, out access) || TryGetValue("*", out access)) + { + return GetAccess(access, out name, namedDecl); + } + + return false; + + bool TryGetValue(string key, out string value) + { + return _config.AccessValues.TryGetValue(key, out value); + } + + bool GetAccess(string access, out AccessSpecifier name, NamedDecl namedDecl) + { + string lowerAccess = access.ToLower(); + switch(lowerAccess) + { + case "public": + { + name = AccessSpecifier.Public; + return true; + } + + case "protected": + { + name = AccessSpecifier.Protected; + return true; + } + + case "protectedinternal": + { + name = AccessSpecifier.ProtectedInternal; + return true; + } + + case "internal": + { + name = AccessSpecifier.Internal; + return true; + } + + case "privateprotected": + { + name = AccessSpecifier.PrivateProtected; + return true; + } + + case "private": + { + name = AccessSpecifier.Private; + return true; + } + + default: + { + name = AccessSpecifier.Internal; + AddDiagnostic(DiagnosticLevel.Warning, $"Unknown access specifier: '{access}' for '{namedDecl}'. Ignoring specifier.", namedDecl); + return false; + } + } + } + } + private AccessSpecifier GetAccessSpecifier(NamedDecl namedDecl) { AccessSpecifier name; + if(GetConfigAccessSpecifier(namedDecl, out name)) + { + return name; + } switch (namedDecl.Access) { diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGeneratorConfiguration.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGeneratorConfiguration.cs index 1cd3e57e..231b4b3d 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGeneratorConfiguration.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGeneratorConfiguration.cs @@ -11,6 +11,7 @@ public sealed class PInvokeGeneratorConfiguration { private const string DefaultMethodClassName = "Methods"; + private readonly Dictionary _accessValues; private readonly Dictionary _remappedNames; private readonly Dictionary> _withAttributes; private readonly Dictionary _withCallConvs; @@ -20,7 +21,7 @@ public sealed class PInvokeGeneratorConfiguration private PInvokeGeneratorConfigurationOptions _options; - public PInvokeGeneratorConfiguration(string libraryPath, string namespaceName, string outputLocation, string testOutputLocation, PInvokeGeneratorOutputMode outputMode = PInvokeGeneratorOutputMode.CSharp, PInvokeGeneratorConfigurationOptions options = PInvokeGeneratorConfigurationOptions.None, string[] excludedNames = null, string headerFile = null, string methodClassName = null, string methodPrefixToStrip = null, IReadOnlyDictionary remappedNames = null, string[] traversalNames = null, IReadOnlyDictionary> withAttributes = null, IReadOnlyDictionary withCallConvs = null, IReadOnlyDictionary withLibraryPaths = null, string[] withSetLastErrors = null, IReadOnlyDictionary withTypes = null, IReadOnlyDictionary> withUsings = null) + public PInvokeGeneratorConfiguration(string libraryPath, string namespaceName, string outputLocation, string testOutputLocation, PInvokeGeneratorOutputMode outputMode = PInvokeGeneratorOutputMode.CSharp, PInvokeGeneratorConfigurationOptions options = PInvokeGeneratorConfigurationOptions.None, IReadOnlyDictionary accessValues = null, string[] excludedNames = null, string headerFile = null, string methodClassName = null, string methodPrefixToStrip = null, IReadOnlyDictionary remappedNames = null, string[] traversalNames = null, IReadOnlyDictionary> withAttributes = null, IReadOnlyDictionary withCallConvs = null, IReadOnlyDictionary withLibraryPaths = null, string[] withSetLastErrors = null, IReadOnlyDictionary withTypes = null, IReadOnlyDictionary> withUsings = null) { if (excludedNames is null) { @@ -83,6 +84,7 @@ public PInvokeGeneratorConfiguration(string libraryPath, string namespaceName, s } _options = options; + _accessValues = new Dictionary(); _remappedNames = new Dictionary(); _withAttributes = new Dictionary>(); _withCallConvs = new Dictionary(); @@ -122,6 +124,7 @@ public PInvokeGeneratorConfiguration(string libraryPath, string namespaceName, s } } + AddRange(_accessValues, accessValues); AddRange(_remappedNames, remappedNames); AddRange(_withAttributes, withAttributes); AddRange(_withCallConvs, withCallConvs); @@ -204,6 +207,8 @@ public bool ExcludeFnptrCodegen public bool GenerateSourceLocationAttribute => _options.HasFlag(PInvokeGeneratorConfigurationOptions.GenerateSourceLocationAttribute); + public IReadOnlyDictionary AccessValues => _accessValues; + public string MethodClassName { get; } public string MethodPrefixToStrip { get;} diff --git a/sources/ClangSharpPInvokeGenerator/Program.cs b/sources/ClangSharpPInvokeGenerator/Program.cs index 0e95960b..f3f7a566 100644 --- a/sources/ClangSharpPInvokeGenerator/Program.cs +++ b/sources/ClangSharpPInvokeGenerator/Program.cs @@ -86,6 +86,7 @@ public static async Task Main(params string[] args) Handler = CommandHandler.Create(typeof(Program).GetMethod(nameof(Run))) }; + AddAccessOption(s_rootCommand); AddAdditionalOption(s_rootCommand); AddConfigOption(s_rootCommand); AddDefineMacroOption(s_rootCommand); @@ -118,6 +119,7 @@ public static async Task Main(params string[] args) public static int Run(InvocationContext context) { + var accessValuePairs = context.ParseResult.ValueForOption("--access"); var additionalArgs = context.ParseResult.ValueForOption("--additional"); var configSwitches = context.ParseResult.ValueForOption("--config"); var defineMacros = context.ParseResult.ValueForOption("--define-macro"); @@ -161,6 +163,7 @@ public static int Run(InvocationContext context) errorList.Add("Error: No output file location provided. Use --output or -o"); } + ParseKeyValuePairs(accessValuePairs, errorList, out Dictionary accessValues); ParseKeyValuePairs(remappedNameValuePairs, errorList, out Dictionary remappedNames); ParseKeyValuePairs(withAttributeNameValuePairs, errorList, out Dictionary> withAttributes); ParseKeyValuePairs(withCallConvNameValuePairs, errorList, out Dictionary withCallConvs); @@ -474,7 +477,7 @@ public static int Run(InvocationContext context) translationFlags |= CXTranslationUnit_Flags.CXTranslationUnit_IncludeAttributedTypes; // Include attributed types in CXType translationFlags |= CXTranslationUnit_Flags.CXTranslationUnit_VisitImplicitAttributes; // Implicit attributes should be visited - var config = new PInvokeGeneratorConfiguration(libraryPath, namespaceName, outputLocation, testOutputLocation, outputMode, configOptions, excludedNames, headerFile, methodClassName, methodPrefixToStrip, remappedNames, traversalNames, withAttributes, withCallConvs, withLibraryPath, withSetLastErrors, withTypes, withUsings); + var config = new PInvokeGeneratorConfiguration(libraryPath, namespaceName, outputLocation, testOutputLocation, outputMode, configOptions, accessValues, excludedNames, headerFile, methodClassName, methodPrefixToStrip, remappedNames, traversalNames, withAttributes, withCallConvs, withLibraryPath, withSetLastErrors, withTypes, withUsings); if (config.GenerateMacroBindings) { @@ -621,6 +624,19 @@ private static void ParseKeyValuePairs(string[] keyValuePairs, List erro } } + private static void AddAccessOption(RootCommand rootCommand) + { + var option = new Option( + aliases: new string[] { "--access", "-ac" }, + description: "Type visibility to use during binding generation.", + argumentType: typeof(string), + getDefaultValue: Array.Empty, + arity: ArgumentArity.OneOrMore + ); + + rootCommand.AddOption(option); + } + private static void AddAdditionalOption(RootCommand rootCommand) { var option = new Option(