From 6684c9c9d9cd0430b7a78f31efa3b9b056a08f2f Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Sun, 23 Jun 2019 13:56:07 -0700 Subject: [PATCH] Some more minor improvements (#75) * Adding a config switch to ignore any default remappings. * Resolve the full path of outputLocation so just 'file.cs' works. * Add a traverse option so you can traverse child includes. * Fixing up CXUnsavedFile and CXTUResourceUsageEntry to use a `nulong` type. * Simplifying and improving how the operator opcode is computed. --- .../PInvokeGenerator.cs | 17 ++++- .../PInvokeGeneratorConfiguration.cs | 28 +++++--- .../PInvokeGeneratorConfigurationOptions.cs | 2 + sources/ClangSharp/Cursors/Cursor.cs | 2 - .../Cursors/Exprs/BinaryOperator.cs | 68 +----------------- .../ClangSharp/Cursors/Exprs/UnaryOperator.cs | 70 ++----------------- .../ClangSharp/Interop.Extensions/CXToken.cs | 30 +++++++- .../Interop.Extensions/CXUnsavedFile.cs | 10 ++- .../Interop/CXTUResourceUsageEntry.cs | 9 ++- sources/ClangSharp/Interop/CXUnsavedFile.cs | 9 ++- sources/ClangSharpPInvokeGenerator/Program.cs | 32 ++++++++- .../FunctionDeclarationBodyImportTest.cs | 42 +++++++++++ 12 files changed, 170 insertions(+), 149 deletions(-) diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs index 0ceceec4..3341cec5 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs @@ -175,9 +175,22 @@ public void GenerateBindings(TranslationUnit translationUnit) foreach (var decl in translationUnitDecl.Decls) { - if (!decl.IsFromMainFile) + if (_config.TraversalNames.Length == 0) { - continue; + if (!decl.Location.IsFromMainFile) + { + continue; + } + } + else + { + decl.Location.GetFileLocation(out CXFile file, out _, out _, out _); + var fileName = file.Name.ToString(); + + if (!_config.TraversalNames.Contains(fileName)) + { + continue; + } } Visit(decl); } diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGeneratorConfiguration.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGeneratorConfiguration.cs index 2310db23..440e190f 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGeneratorConfiguration.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGeneratorConfiguration.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IO; namespace ClangSharp { @@ -10,7 +11,7 @@ public sealed class PInvokeGeneratorConfiguration private readonly Dictionary _remappedNames; private readonly PInvokeGeneratorConfigurationOptions _options; - public PInvokeGeneratorConfiguration(string libraryPath, string namespaceName, string outputLocation, PInvokeGeneratorConfigurationOptions options = PInvokeGeneratorConfigurationOptions.None, string[] excludedNames = null, string methodClassName = null, string methodPrefixToStrip = null, IReadOnlyDictionary remappedNames = null) + public PInvokeGeneratorConfiguration(string libraryPath, string namespaceName, string outputLocation, PInvokeGeneratorConfigurationOptions options = PInvokeGeneratorConfigurationOptions.None, string[] excludedNames = null, string methodClassName = null, string methodPrefixToStrip = null, IReadOnlyDictionary remappedNames = null, string[] traversalNames = null) { if (excludedNames is null) { @@ -42,6 +43,11 @@ public PInvokeGeneratorConfiguration(string libraryPath, string namespaceName, s throw new ArgumentNullException(nameof(outputLocation)); } + if (traversalNames is null) + { + traversalNames = Array.Empty(); + } + _options = options; ExcludedNames = excludedNames; @@ -49,15 +55,19 @@ public PInvokeGeneratorConfiguration(string libraryPath, string namespaceName, s MethodClassName = methodClassName; MethodPrefixToStrip = methodPrefixToStrip; Namespace = namespaceName; - OutputLocation = outputLocation; + OutputLocation = Path.GetFullPath(outputLocation); + TraversalNames = traversalNames; - _remappedNames = new Dictionary() + if (!_options.HasFlag(PInvokeGeneratorConfigurationOptions.NoDefaultRemappings)) { - ["intptr_t"] = "IntPtr", - ["ptrdiff_t"] = "IntPtr", - ["size_t"] = "UIntPtr", - ["uintptr_t"] = "UIntPtr", - }; + _remappedNames = new Dictionary() + { + ["intptr_t"] = "IntPtr", + ["ptrdiff_t"] = "IntPtr", + ["size_t"] = "UIntPtr", + ["uintptr_t"] = "UIntPtr", + }; + } if (remappedNames != null) { @@ -87,5 +97,7 @@ public PInvokeGeneratorConfiguration(string libraryPath, string namespaceName, s public string OutputLocation { get; } public IReadOnlyDictionary RemappedNames => _remappedNames; + + public string[] TraversalNames { get; } } } diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGeneratorConfigurationOptions.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGeneratorConfigurationOptions.cs index 6654b567..0af2ff86 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGeneratorConfigurationOptions.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGeneratorConfigurationOptions.cs @@ -10,5 +10,7 @@ public enum PInvokeGeneratorConfigurationOptions GenerateMultipleFiles = 0x00000001, GenerateUnixTypes = 0x00000002, + + NoDefaultRemappings = 0x00000004, } } diff --git a/sources/ClangSharp/Cursors/Cursor.cs b/sources/ClangSharp/Cursors/Cursor.cs index 0fa5ad1d..417557a3 100644 --- a/sources/ClangSharp/Cursors/Cursor.cs +++ b/sources/ClangSharp/Cursors/Cursor.cs @@ -42,8 +42,6 @@ private protected Cursor(CXCursor handle, CXCursorKind expectedKind) public CXCursor Handle { get; } - public bool IsFromMainFile => Location.IsFromMainFile; - public CXCursorKind Kind => Handle.Kind; public string KindSpelling => Handle.KindSpelling.ToString(); diff --git a/sources/ClangSharp/Cursors/Exprs/BinaryOperator.cs b/sources/ClangSharp/Cursors/Exprs/BinaryOperator.cs index fa9d1cc9..03f57f73 100644 --- a/sources/ClangSharp/Cursors/Exprs/BinaryOperator.cs +++ b/sources/ClangSharp/Cursors/Exprs/BinaryOperator.cs @@ -28,75 +28,13 @@ internal BinaryOperator(CXCursor handle, CXCursorKind expectedKind) : base(handl protected virtual string GetOpcode() { - var tokens = Handle.TranslationUnit.Tokenize(Extent); + var lhsTokens = Handle.TranslationUnit.Tokenize(LHS.Extent); + var tokens = Handle.TranslationUnit.Tokenize(Extent); Debug.Assert(tokens.Length >= 3); - int operatorIndex = -1; - int parenDepth = 0; - - for (int index = 0; (index < tokens.Length) && (operatorIndex == -1); index++) - { - var token = tokens[index]; - - if (token.Kind != CXTokenKind.CXToken_Punctuation) - { - continue; - } - - var punctuation = tokens[index].GetSpelling(Handle.TranslationUnit).ToString(); - - switch (punctuation) - { - case "!=": - case "%": - case "&": - case "&&": - case "*": - case "+": - case "-": - case "/": - case "<": - case "<<": - case "<=": - case "=": - case "==": - case ">": - case ">>": - case ">=": - case "^": - case "|": - case "||": - { - if (parenDepth == 0) - { - operatorIndex = index; - } - break; - } - - case "(": - { - parenDepth++; - break; - } - - case ")": - { - parenDepth--; - break; - } - - default: - { - Debug.WriteLine($"Unhandled punctuation kind: {punctuation}."); - Debugger.Break(); - break; - } - } - } + int operatorIndex = lhsTokens.Length; - Debug.Assert(operatorIndex != -1); Debug.Assert(tokens[operatorIndex].Kind == CXTokenKind.CXToken_Punctuation); return tokens[operatorIndex].GetSpelling(Handle.TranslationUnit).ToString(); } diff --git a/sources/ClangSharp/Cursors/Exprs/UnaryOperator.cs b/sources/ClangSharp/Cursors/Exprs/UnaryOperator.cs index a646d9cf..32613361 100644 --- a/sources/ClangSharp/Cursors/Exprs/UnaryOperator.cs +++ b/sources/ClangSharp/Cursors/Exprs/UnaryOperator.cs @@ -26,77 +26,15 @@ internal UnaryOperator(CXCursor handle) : base(handle, CXCursorKind.CXCursor_Una private (string Opcode, bool IsPrefix) GetOpcode() { - var tokens = Handle.TranslationUnit.Tokenize(Extent); + var subExprTokens = Handle.TranslationUnit.Tokenize(SubExpr.Extent); + var tokens = Handle.TranslationUnit.Tokenize(Extent); Debug.Assert(tokens.Length >= 2); - int operatorIndex = -1; - int parenDepth = 0; - bool isPrefix = false; - - for (int index = 0; (index < tokens.Length) && (operatorIndex == -1); index++) - { - var token = tokens[index]; - - if (token.Kind != CXTokenKind.CXToken_Punctuation) - { - continue; - } - - var punctuation = tokens[index].GetSpelling(Handle.TranslationUnit).ToString(); - - switch (punctuation) - { - case "!": - case "&": - case "*": - case "+": - case "-": - case "~": - { - if (parenDepth == 0) - { - operatorIndex = index; - isPrefix = true; - } - break; - } + bool isPrefix = tokens[0] != subExprTokens[0]; + int operatorIndex = isPrefix ? 0 : subExprTokens.Length; - case "(": - { - parenDepth++; - break; - } - - case ")": - { - parenDepth--; - break; - } - - case "++": - case "--": - { - if (parenDepth == 0) - { - operatorIndex = index; - isPrefix = ((index + 1) != tokens.Length); - } - break; - } - - default: - { - Debug.WriteLine($"Unhandled punctuation kind: {punctuation}."); - Debugger.Break(); - break; - } - } - } - - Debug.Assert(operatorIndex != -1); Debug.Assert(tokens[operatorIndex].Kind == CXTokenKind.CXToken_Punctuation); - var opcode = tokens[operatorIndex].GetSpelling(Handle.TranslationUnit).ToString(); return (opcode, isPrefix); } diff --git a/sources/ClangSharp/Interop.Extensions/CXToken.cs b/sources/ClangSharp/Interop.Extensions/CXToken.cs index e90af24f..12033621 100644 --- a/sources/ClangSharp/Interop.Extensions/CXToken.cs +++ b/sources/ClangSharp/Interop.Extensions/CXToken.cs @@ -1,13 +1,41 @@ +using System; + namespace ClangSharp.Interop { - public unsafe partial struct CXToken + public unsafe partial struct CXToken : IEquatable { public CXTokenKind Kind => clang.getTokenKind(this); + public static bool operator ==(CXToken left, CXToken right) + { + return (left.int_data[0] == right.int_data[0]) && + (left.int_data[1] == right.int_data[1]) && + (left.int_data[2] == right.int_data[2]) && + (left.int_data[3] == right.int_data[3]) && + (left.ptr_data == right.ptr_data); + } + + public static bool operator !=(CXToken left, CXToken right) + { + return (left.int_data[0] != right.int_data[0]) || + (left.int_data[1] != right.int_data[1]) || + (left.int_data[2] != right.int_data[2]) || + (left.int_data[3] != right.int_data[3]) || + (left.ptr_data != right.ptr_data); + } + + public override bool Equals(object obj) => (obj is CXSourceRange other) && Equals(other); + + public bool Equals(CXToken other) => this == other; + public CXSourceRange GetExtent(CXTranslationUnit translationUnit) => clang.getTokenExtent(translationUnit, this); + public override int GetHashCode() => HashCode.Combine(int_data[0], int_data[1], int_data[2], int_data[3], (IntPtr)ptr_data); + public CXSourceLocation GetLocation(CXTranslationUnit translationUnit) => clang.getTokenLocation(translationUnit, this); public CXString GetSpelling(CXTranslationUnit translationUnit) => clang.getTokenSpelling(translationUnit, this); + + public override string ToString() => ""; } } diff --git a/sources/ClangSharp/Interop.Extensions/CXUnsavedFile.cs b/sources/ClangSharp/Interop.Extensions/CXUnsavedFile.cs index 44672f0c..fc7aaad8 100644 --- a/sources/ClangSharp/Interop.Extensions/CXUnsavedFile.cs +++ b/sources/ClangSharp/Interop.Extensions/CXUnsavedFile.cs @@ -2,6 +2,12 @@ using System.Runtime.InteropServices; using System.Text; +#if Windows_NT +using nulong = System.UInt32; +#else +using nulong = System.UIntPtr; +#endif + namespace ClangSharp.Interop { public unsafe partial struct CXUnsavedFile : IDisposable @@ -43,7 +49,7 @@ public static CXUnsavedFile Create(string filename, string contents) { Filename = (sbyte*)pFilename, Contents = (sbyte*)pContents, - Length = (uint)contentsLength + Length = (nulong)contentsLength }; } @@ -59,7 +65,7 @@ public void Dispose() { Marshal.FreeHGlobal((IntPtr)Contents); Contents = null; - Length = 0; + Length = (nulong)0; } } diff --git a/sources/ClangSharp/Interop/CXTUResourceUsageEntry.cs b/sources/ClangSharp/Interop/CXTUResourceUsageEntry.cs index c311bfa0..97b8a908 100644 --- a/sources/ClangSharp/Interop/CXTUResourceUsageEntry.cs +++ b/sources/ClangSharp/Interop/CXTUResourceUsageEntry.cs @@ -1,3 +1,10 @@ +#if Windows_NT +using nulong = System.UInt32; +#else +using System; +using nulong = System.UIntPtr; +#endif + namespace ClangSharp.Interop { public partial struct CXTUResourceUsageEntry @@ -6,6 +13,6 @@ public partial struct CXTUResourceUsageEntry public CXTUResourceUsageKind kind; [NativeTypeName("unsigned long")] - public uint amount; + public nulong amount; } } diff --git a/sources/ClangSharp/Interop/CXUnsavedFile.cs b/sources/ClangSharp/Interop/CXUnsavedFile.cs index 60c316a3..edd8a1d0 100644 --- a/sources/ClangSharp/Interop/CXUnsavedFile.cs +++ b/sources/ClangSharp/Interop/CXUnsavedFile.cs @@ -1,3 +1,10 @@ +#if Windows_NT +using nulong = System.UInt32; +#else +using System; +using nulong = System.UIntPtr; +#endif + namespace ClangSharp.Interop { public unsafe partial struct CXUnsavedFile @@ -9,6 +16,6 @@ public unsafe partial struct CXUnsavedFile public sbyte* Contents; [NativeTypeName("unsigned long")] - public uint Length; + public nulong Length; } } diff --git a/sources/ClangSharpPInvokeGenerator/Program.cs b/sources/ClangSharpPInvokeGenerator/Program.cs index fff50f8e..26269822 100644 --- a/sources/ClangSharpPInvokeGenerator/Program.cs +++ b/sources/ClangSharpPInvokeGenerator/Program.cs @@ -32,6 +32,7 @@ public static async Task Main(params string[] args) AddOutputOption(s_rootCommand); AddPrefixStripOption(s_rootCommand); AddRemapOption(s_rootCommand); + AddTraverseOption(s_rootCommand); } return await s_rootCommand.InvokeAsync(args); } @@ -50,6 +51,7 @@ public static int Run(InvocationContext context) var namespaceName = context.ParseResult.ValueForOption("namespace"); var outputLocation = context.ParseResult.ValueForOption("output"); var remappedNameValuePairs = context.ParseResult.ValueForOption("remap"); + var traversalNames = context.ParseResult.ValueForOption("traverse"); var errorList = new List(); @@ -94,12 +96,24 @@ public static int Run(InvocationContext context) { switch (configSwitch) { + case "default-remappings": + { + configOptions &= ~PInvokeGeneratorConfigurationOptions.NoDefaultRemappings; + break; + } + case "multi-file": { configOptions |= PInvokeGeneratorConfigurationOptions.GenerateMultipleFiles; break; } + case "no-default-remappings": + { + configOptions |= PInvokeGeneratorConfigurationOptions.NoDefaultRemappings; + break; + } + case "single-file": { configOptions &= ~PInvokeGeneratorConfigurationOptions.GenerateMultipleFiles; @@ -154,7 +168,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, configOptions, excludedNames, methodClassName, methodPrefixToStrip, remappedNames); + var config = new PInvokeGeneratorConfiguration(libraryPath, namespaceName, outputLocation, configOptions, excludedNames, methodClassName, methodPrefixToStrip, remappedNames, traversalNames); int exitCode = 0; @@ -414,5 +428,21 @@ private static void AddRemapOption(RootCommand rootCommand) rootCommand.AddOption(option); } + + private static void AddTraverseOption(RootCommand rootCommand) + { + var argument = new Argument + { + ArgumentType = typeof(string), + Arity = ArgumentArity.OneOrMore, + Name = "name" + }; + argument.SetDefaultValue(Array.Empty()); + + var option = new Option("--traverse", "A file name included either directly or indirectly by -f that should be traversed during binding generation.", argument); + option.AddAlias("-t"); + + rootCommand.AddOption(option); + } } } diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/FunctionDeclarationBodyImportTest.cs b/tests/ClangSharp.PInvokeGenerator.UnitTests/FunctionDeclarationBodyImportTest.cs index 0a3709c2..d9c2dc04 100644 --- a/tests/ClangSharp.PInvokeGenerator.UnitTests/FunctionDeclarationBodyImportTest.cs +++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/FunctionDeclarationBodyImportTest.cs @@ -144,6 +144,48 @@ public static void MyFunction() await ValidateGeneratedBindings(inputContents, expectedOutputContents); } + [Fact] + public async Task CompareMultipleEnumTest() + { + var inputContents = @"enum MyEnum : int +{ + MyEnum_Value0, + MyEnum_Value1, + MyEnum_Value2, +}; + +static inline int MyFunction(MyEnum x) +{ + return x == MyEnum_Value0 || + x == MyEnum_Value1 || + x == MyEnum_Value2; +} +"; + + var expectedOutputContents = @"namespace ClangSharp.Test +{ + public enum MyEnum + { + MyEnum_Value0, + MyEnum_Value1, + MyEnum_Value2, + } + + public static partial class Methods + { + private const string libraryPath = ""ClangSharpPInvokeGenerator""; + + public static int MyFunction(MyEnum x) + { + return x == MyEnum_Value0 || x == MyEnum_Value1 || x == MyEnum_Value2; + } + } +} +"; + + await ValidateGeneratedBindings(inputContents, expectedOutputContents); + } + [Fact] public async Task ReturnIntegerTest() {