Update dependencies and fix some functionality (#140)

* Removing dependency on Microsoft.Net.Compilers.Toolset

* Updating dependencies to their latest versions

* Removing a System.CommandLine workaround for the argument arity

* Make the --library optional

* Adding support for with-setlasterror

* Fixing function pointers to not unnecessarily add an indirection

* Ensure ReferenceTypes are dereferenced using ->

* Don't use pointers for the fixed sized-buffer ref codegen
This commit is contained in:
Tanner Gooding 2020-05-22 07:55:31 -07:00 коммит произвёл GitHub
Родитель 05c0ee0ab2
Коммит 0ca80de67d
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
14 изменённых файлов: 357 добавлений и 84 удалений

Просмотреть файл

@ -65,9 +65,4 @@
<UseSharedCompilation>true</UseSharedCompilation>
</PropertyGroup>
<!-- Package references which are consumed by all projects -->
<ItemGroup>
<PackageReference Include="Microsoft.Net.Compilers.Toolset" IsImplicitlyDefined="true" PrivateAssets="all" />
</ItemGroup>
</Project>

Просмотреть файл

@ -30,9 +30,8 @@
<PackageReference Update="libClang" Version="10.0.0" />
<PackageReference Update="libClangSharp" Version="10.0.0-beta1" />
<PackageReference Update="Microsoft.Bcl.HashCode" Version="1.1.0" />
<PackageReference Update="Microsoft.Net.Compilers.Toolset" Version="3.5.0" />
<PackageReference Update="Microsoft.NET.Test.Sdk" Version="16.5.0" />
<PackageReference Update="System.CommandLine" Version="2.0.0-beta1.20158.1" />
<PackageReference Update="Microsoft.NET.Test.Sdk" Version="16.6.1" />
<PackageReference Update="System.CommandLine" Version="2.0.0-beta1.20253.1" />
<PackageReference Update="System.Memory" Version="4.5.4" />
<PackageReference Update="xunit" Version="2.4.1" />
<PackageReference Update="xunit.runner.visualstudio" Version="2.4.1" />

Просмотреть файл

@ -457,7 +457,7 @@ namespace ClangSharp
if (isVirtual)
{
Debug.Assert(!_config.GeneratePreviewCode);
Debug.Assert(!_config.GeneratePreviewCodeFnptr);
_outputBuilder.AddUsingDirective("System.Runtime.InteropServices");
@ -490,7 +490,9 @@ namespace ClangSharp
_outputBuilder.Write(cxxMethodDecl.Handle.Mangling);
}
_outputBuilder.WriteLine("\", ExactSpelling = true)]");
_outputBuilder.Write("\", ExactSpelling = true");
WithSetLastError(name);
_outputBuilder.WriteLine(")]");
}
var returnType = functionDecl.ReturnType;
@ -1034,7 +1036,7 @@ namespace ClangSharp
pinvokeGenerator._visitedDecls.Add(cxxMethodDecl);
if (!pinvokeGenerator._config.GeneratePreviewCode)
if (!pinvokeGenerator._config.GeneratePreviewCodeFnptr)
{
pinvokeGenerator._outputBuilder.NeedsNewline = true;
@ -1217,7 +1219,7 @@ namespace ClangSharp
outputBuilder.Write('*');
}
if (!pinvokeGenerator._config.GeneratePreviewCode)
if (!pinvokeGenerator._config.GeneratePreviewCodeFnptr)
{
outputBuilder.Write("Marshal.GetDelegateForFunctionPointer<");
outputBuilder.Write(pinvokeGenerator.PrefixAndStripName(cxxMethodDeclName));
@ -1228,7 +1230,7 @@ namespace ClangSharp
outputBuilder.Write("lpVtbl->");
outputBuilder.Write(pinvokeGenerator.EscapeAndStripName(cxxMethodDeclName));
if (!pinvokeGenerator._config.GeneratePreviewCode)
if (!pinvokeGenerator._config.GeneratePreviewCodeFnptr)
{
outputBuilder.Write(')');
}
@ -1570,18 +1572,16 @@ namespace ClangSharp
{
return;
}
bool isUnsafe = typeName.Contains('*');
if (typeName.Contains('*'))
{
outputBuilder.AddUsingDirective("System");
typeName = "IntPtr";
}
outputBuilder.NeedsNewline = true;
outputBuilder.WriteIndented(pinvokeGenerator.GetAccessSpecifierName(constantArray));
outputBuilder.Write(' ');
if (isUnsafe)
{
outputBuilder.Write("unsafe");
outputBuilder.Write(' ');
}
outputBuilder.Write("partial struct");
outputBuilder.Write(' ');
outputBuilder.WriteLine(pinvokeGenerator.GetArtificalFixedSizedBufferName(constantArray));
@ -1642,11 +1642,8 @@ namespace ClangSharp
if (pinvokeGenerator._config.GenerateCompatibleCode)
{
if (!isUnsafe)
{
outputBuilder.Write("unsafe");
outputBuilder.Write(' ');
}
outputBuilder.Write("unsafe");
outputBuilder.Write(' ');
}
else
{
@ -1766,7 +1763,7 @@ namespace ClangSharp
}
else if (pointeeType is FunctionProtoType functionProtoType)
{
if (_config.GeneratePreviewCode)
if (_config.GeneratePreviewCodeFnptr)
{
return;
}

Просмотреть файл

@ -3,6 +3,7 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using ClangSharp.Interop;
namespace ClangSharp
@ -587,7 +588,22 @@ namespace ClangSharp
{
Visit(memberExpr.Base);
if ((memberExpr.Base.Type is PointerType) && !(memberExpr.Base is CXXThisExpr))
bool isPointerType;
if (memberExpr.Base is CXXThisExpr)
{
isPointerType = false;
}
else if (memberExpr.Base is DeclRefExpr declRefExpr)
{
isPointerType = (declRefExpr.Decl.Type is PointerType) || (declRefExpr.Decl.Type is ReferenceType);
}
else
{
isPointerType = (memberExpr.Base.Type is PointerType) || (memberExpr.Base.Type is ReferenceType);
}
if (isPointerType)
{
_outputBuilder.Write('-');
_outputBuilder.Write('>');

Просмотреть файл

@ -310,14 +310,17 @@ namespace ClangSharp
indentationString += outputBuilder.IndentationString;
sw.Write(indentationString);
sw.Write("private const string LibraryPath =");
sw.Write(' ');
sw.Write('"');
sw.Write(Config.LibraryPath);
sw.Write('"');
sw.WriteLine(';');
sw.WriteLine();
if (!string.IsNullOrWhiteSpace(Config.LibraryPath))
{
sw.Write(indentationString);
sw.Write("private const string LibraryPath =");
sw.Write(' ');
sw.Write('"');
sw.Write(Config.LibraryPath);
sw.Write('"');
sw.WriteLine(';');
sw.WriteLine();
}
}
foreach (var line in outputBuilder.Contents)
@ -766,7 +769,7 @@ namespace ClangSharp
{
if (_config.GenerateUnixTypes)
{
name = _config.GeneratePreviewCode ? "nuint" : "UIntPtr";
name = _config.GeneratePreviewCodeNint ? "nuint" : "UIntPtr";
}
else
{
@ -816,7 +819,7 @@ namespace ClangSharp
{
if (_config.GenerateUnixTypes)
{
name = _config.GeneratePreviewCode ? "nint" : "IntPtr";
name = _config.GeneratePreviewCodeNint ? "nint" : "IntPtr";
}
else
{
@ -856,33 +859,7 @@ namespace ClangSharp
}
else if (type is FunctionType functionType)
{
if (_config.GeneratePreviewCode && (functionType is FunctionProtoType functionProtoType))
{
var remappedName = GetRemappedName(name, cursor, tryRemapOperatorName: false);
var callConv = GetCallingConventionName(cursor, functionType.CallConv, remappedName).ToLower();
var nameBuilder = new StringBuilder();
nameBuilder.Append("delegate");
nameBuilder.Append('*');
nameBuilder.Append(' ');
nameBuilder.Append((callConv != "winapi") ? callConv : "unmanaged");
nameBuilder.Append('<');
foreach (var paramType in functionProtoType.ParamTypes)
{
nameBuilder.Append(GetRemappedTypeName(cursor, paramType, out _));
nameBuilder.Append(',');
nameBuilder.Append(' ');
}
nameBuilder.Append(GetRemappedTypeName(cursor, functionType.ReturnType, out _));
nameBuilder.Append('>');
name = nameBuilder.ToString();
}
else
{
name = "IntPtr";
}
name = GetTypeNameForPointeeType(cursor, functionType, out var nativeFunctionTypeName);
}
else if (type is PointerType pointerType)
{
@ -948,6 +925,36 @@ namespace ClangSharp
{
name = GetTypeNameForPointeeType(cursor, attributedType.ModifiedType, out var nativeModifiedTypeName);
}
else if (pointeeType is FunctionType functionType)
{
if (_config.GeneratePreviewCodeFnptr && (functionType is FunctionProtoType functionProtoType))
{
var remappedName = GetRemappedName(name, cursor, tryRemapOperatorName: false);
var callConv = GetCallingConventionName(cursor, functionType.CallConv, remappedName).ToLower();
var nameBuilder = new StringBuilder();
nameBuilder.Append("delegate");
nameBuilder.Append('*');
nameBuilder.Append(' ');
nameBuilder.Append((callConv != "winapi") ? callConv : "unmanaged");
nameBuilder.Append('<');
foreach (var paramType in functionProtoType.ParamTypes)
{
nameBuilder.Append(GetRemappedTypeName(cursor, paramType, out _));
nameBuilder.Append(',');
nameBuilder.Append(' ');
}
nameBuilder.Append(GetRemappedTypeName(cursor, functionType.ReturnType, out _));
nameBuilder.Append('>');
name = nameBuilder.ToString();
}
else
{
name = "IntPtr";
}
}
else
{
name = GetTypeName(cursor, pointeeType, out nativePointeeTypeName);
@ -1397,6 +1404,14 @@ namespace ClangSharp
}
}
private void WithSetLastError(string remappedName)
{
if (_config.WithSetLastErrors.Contains("*") || _config.WithSetLastErrors.Contains(remappedName))
{
_outputBuilder.Write(", SetLastError = true");
}
}
private void WithType(string remappedName, ref string integerTypeName, ref string nativeTypeName)
{
if (_config.WithTypes.TryGetValue(remappedName, out string type))

Просмотреть файл

@ -18,7 +18,7 @@ namespace ClangSharp
private readonly Dictionary<string, IReadOnlyList<string>> _withUsings;
private readonly PInvokeGeneratorConfigurationOptions _options;
public PInvokeGeneratorConfiguration(string libraryPath, string namespaceName, string outputLocation, PInvokeGeneratorConfigurationOptions options = PInvokeGeneratorConfigurationOptions.None, string[] excludedNames = null, string headerFile = null, string methodClassName = null, string methodPrefixToStrip = null, IReadOnlyDictionary<string, string> remappedNames = null, string[] traversalNames = null, IReadOnlyDictionary<string, IReadOnlyList<string>> withAttributes = null, IReadOnlyDictionary<string, string> withCallConvs = null, IReadOnlyDictionary<string, string> withTypes = null, IReadOnlyDictionary<string, IReadOnlyList<string>> withUsings = null)
public PInvokeGeneratorConfiguration(string libraryPath, string namespaceName, string outputLocation, PInvokeGeneratorConfigurationOptions options = PInvokeGeneratorConfigurationOptions.None, string[] excludedNames = null, string headerFile = null, string methodClassName = null, string methodPrefixToStrip = null, IReadOnlyDictionary<string, string> remappedNames = null, string[] traversalNames = null, IReadOnlyDictionary<string, IReadOnlyList<string>> withAttributes = null, IReadOnlyDictionary<string, string> withCallConvs = null, string[] withSetLastErrors = null, IReadOnlyDictionary<string, string> withTypes = null, IReadOnlyDictionary<string, IReadOnlyList<string>> withUsings = null)
{
if (excludedNames is null)
{
@ -27,7 +27,7 @@ namespace ClangSharp
if (string.IsNullOrWhiteSpace(libraryPath))
{
throw new ArgumentNullException(nameof(libraryPath));
libraryPath = string.Empty;
}
if (string.IsNullOrWhiteSpace(methodClassName))
@ -60,6 +60,11 @@ namespace ClangSharp
traversalNames = Array.Empty<string>();
}
if (withSetLastErrors is null)
{
withSetLastErrors = Array.Empty<string>();
}
_options = options;
_remappedNames = new Dictionary<string, string>();
_withAttributes = new Dictionary<string, IReadOnlyList<string>>();
@ -77,10 +82,11 @@ namespace ClangSharp
// Normalize the traversal names to use \ rather than / so path comparisons are simpler
TraversalNames = traversalNames.Select(traversalName => traversalName.Replace('\\', '/')).ToArray();
WithSetLastErrors = withSetLastErrors;
if (!_options.HasFlag(PInvokeGeneratorConfigurationOptions.NoDefaultRemappings))
{
if (GeneratePreviewCode)
if (GeneratePreviewCodeNint)
{
_remappedNames.Add("intptr_t", "nint");
_remappedNames.Add("ptrdiff_t", "nint");
@ -107,7 +113,9 @@ namespace ClangSharp
public bool GenerateCompatibleCode => _options.HasFlag(PInvokeGeneratorConfigurationOptions.GenerateCompatibleCode);
public bool GeneratePreviewCode => _options.HasFlag(PInvokeGeneratorConfigurationOptions.GeneratePreviewCode);
public bool GeneratePreviewCodeFnptr => _options.HasFlag(PInvokeGeneratorConfigurationOptions.GeneratePreviewCodeFnptr);
public bool GeneratePreviewCodeNint => _options.HasFlag(PInvokeGeneratorConfigurationOptions.GeneratePreviewCodeNint);
public bool GenerateMultipleFiles => _options.HasFlag(PInvokeGeneratorConfigurationOptions.GenerateMultipleFiles);
@ -133,6 +141,8 @@ namespace ClangSharp
public IReadOnlyDictionary<string, string> WithCallConvs => _withCallConvs;
public string[] WithSetLastErrors { get; }
public IReadOnlyDictionary<string, string> WithTypes => _withTypes;
public IReadOnlyDictionary<string, IReadOnlyList<string>> WithUsings => _withUsings;

Просмотреть файл

@ -17,6 +17,10 @@ namespace ClangSharp
GenerateCompatibleCode = 0x00000008,
GeneratePreviewCode = 0x00000010,
GeneratePreviewCodeNint = 0x00000010,
GeneratePreviewCodeFnptr = 0x00000020,
GeneratePreviewCode = GeneratePreviewCodeNint | GeneratePreviewCodeFnptr,
}
}

Просмотреть файл

@ -43,6 +43,7 @@ namespace ClangSharp
AddTraverseOption(s_rootCommand);
AddWithAttributeOption(s_rootCommand);
AddWithCallConvOption(s_rootCommand);
AddWithSetLastErrorOption(s_rootCommand);
AddWithTypeOption(s_rootCommand);
AddWithUsingOption(s_rootCommand);
@ -70,6 +71,7 @@ namespace ClangSharp
var traversalNames = context.ParseResult.ValueForOption<string[]>("traverse");
var withAttributeNameValuePairs = context.ParseResult.ValueForOption<string[]>("with-attribute");
var withCallConvNameValuePairs = context.ParseResult.ValueForOption<string[]>("with-callconv");
var withSetLastErrors = context.ParseResult.ValueForOption<string[]>("with-setlasterror");
var withTypeNameValuePairs = context.ParseResult.ValueForOption<string[]>("with-type");
var withUsingNameValuePairs = context.ParseResult.ValueForOption<string[]>("with-using");
@ -80,11 +82,6 @@ namespace ClangSharp
errorList.Add("Error: No input C/C++ files provided. Use --file or -f");
}
if (string.IsNullOrWhiteSpace(libraryPath))
{
errorList.Add("Error: No library path location provided. Use --libraryPath or -l");
}
if (string.IsNullOrWhiteSpace(namespaceName))
{
errorList.Add("Error: No namespace provided. Use --namespace or -n");
@ -96,8 +93,8 @@ namespace ClangSharp
}
ParseKeyValuePairs(remappedNameValuePairs, errorList, out Dictionary<string, string> remappedNames);
ParseKeyValuePairs(withCallConvNameValuePairs, errorList, out Dictionary<string, string> withCallConvs);
ParseKeyValuePairs(withAttributeNameValuePairs, errorList, out Dictionary<string, IReadOnlyList<string>> withAttributes);
ParseKeyValuePairs(withCallConvNameValuePairs, errorList, out Dictionary<string, string> withCallConvs);
ParseKeyValuePairs(withTypeNameValuePairs, errorList, out Dictionary<string, string> withTypes);
ParseKeyValuePairs(withUsingNameValuePairs, errorList, out Dictionary<string, IReadOnlyList<string>> withUsings);
@ -146,6 +143,20 @@ namespace ClangSharp
break;
}
case "preview-codegen-nint":
{
configOptions &= ~PInvokeGeneratorConfigurationOptions.GenerateCompatibleCode;
configOptions |= PInvokeGeneratorConfigurationOptions.GeneratePreviewCodeNint;
break;
}
case "preview-codegen-fnptr":
{
configOptions &= ~PInvokeGeneratorConfigurationOptions.GenerateCompatibleCode;
configOptions |= PInvokeGeneratorConfigurationOptions.GeneratePreviewCodeFnptr;
break;
}
case "single-file":
{
configOptions &= ~PInvokeGeneratorConfigurationOptions.GenerateMultipleFiles;
@ -201,7 +212,7 @@ namespace ClangSharp
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, headerFile, methodClassName, methodPrefixToStrip, remappedNames, traversalNames, withAttributes, withCallConvs, withTypes, withUsings);
var config = new PInvokeGeneratorConfiguration(libraryPath, namespaceName, outputLocation, configOptions, excludedNames, headerFile, methodClassName, methodPrefixToStrip, remappedNames, traversalNames, withAttributes, withCallConvs, withSetLastErrors, withTypes, withUsings);
int exitCode = 0;
@ -553,7 +564,7 @@ namespace ClangSharp
Argument = new Argument("<name>=<value>")
{
ArgumentType = typeof(string),
Arity = new ArgumentArity(1, ushort.MaxValue),
Arity = ArgumentArity.OneOrMore,
}
};
option.Argument.SetDefaultValue(Array.Empty<string>());
@ -621,6 +632,21 @@ namespace ClangSharp
rootCommand.AddOption(option);
}
private static void AddWithSetLastErrorOption(RootCommand rootCommand)
{
var option = new Option(new string[] { "--with-setlasterror", "-wsle" }, "Add the SetLastError=true modifier to a given DllImport or UnmanagedFunctionPointer.")
{
Argument = new Argument("<remapped-name>=<value>")
{
ArgumentType = typeof(string),
Arity = ArgumentArity.OneOrMore,
}
};
option.Argument.SetDefaultValue(Array.Empty<string>());
rootCommand.AddOption(option);
}
private static void AddWithTypeOption(RootCommand rootCommand)
{
var option = new Option(new string[] { "--with-type", "-wt" }, "A type to be used for the given enum declaration during binding generation.")

Просмотреть файл

@ -11,6 +11,11 @@
"GenerateLLVM": {
"commandName": "Project",
"commandLineArgs": "\"@$(MSBuildProjectDirectory)/Properties/GenerateLLVM.rsp\" --file-directory \"$(LLVMIncludePath)\" --include-directory \"$(LLVMIncludePath)\" --libraryPath $(LibLLVMName)"
},
"GenerateTerraFX": {
"commandName": "Project",
"commandLineArgs": "@generate.rsp",
"workingDirectory": "D:\\tagoo\\Repos\\terrafx.interop.windows\\generation\\um\\winnt"
}
}
}

Просмотреть файл

@ -1339,6 +1339,41 @@ int MyFunction2(MyStruct* instance)
await ValidateGeneratedBindings(inputContents, expectedOutputContents);
}
[Fact]
public async Task RefToPtrTest()
{
var inputContents = @"struct MyStruct {
int value;
};
bool MyFunction(const MyStruct& lhs, const MyStruct& rhs)
{
return lhs.value == rhs.value;
}
";
var expectedOutputContents = @"namespace ClangSharp.Test
{
public partial struct MyStruct
{
public int value;
}
public static unsafe partial class Methods
{
private const string LibraryPath = ""ClangSharpPInvokeGenerator"";
public static bool MyFunction([NativeTypeName(""const MyStruct &"")] MyStruct* lhs, [NativeTypeName(""const MyStruct &"")] MyStruct* rhs)
{
return lhs->value == rhs->value;
}
}
}
";
await ValidateGeneratedBindings(inputContents, expectedOutputContents);
}
[Fact]
public async Task ReturnCXXNullPtrTest()
{

Просмотреть файл

@ -52,6 +52,49 @@ namespace ClangSharp.Test
await ValidateGeneratedBindings(inputContents, expectedOutputContents);
}
[Fact]
public async Task FunctionPointerParameterTest()
{
var inputContents = @"void MyFunction(void (*callback)());";
var expectedOutputContents = @"using System;
using System.Runtime.InteropServices;
namespace ClangSharp.Test
{
public static partial class Methods
{
private const string LibraryPath = ""ClangSharpPInvokeGenerator"";
[DllImport(LibraryPath, CallingConvention = CallingConvention.Cdecl, EntryPoint = ""MyFunction"", ExactSpelling = true)]
public static extern void MyFunction([NativeTypeName(""void (*)()"")] IntPtr callback);
}
}
";
await ValidateGeneratedBindings(inputContents, expectedOutputContents);
}
[Fact]
public async Task NoLibraryPathTest()
{
var inputContents = @"void MyFunction();";
var expectedOutputContents = @"using System.Runtime.InteropServices;
namespace ClangSharp.Test
{
public static partial class Methods
{
[DllImport(LibraryPath, CallingConvention = CallingConvention.Cdecl, EntryPoint = ""MyFunction"", ExactSpelling = true)]
public static extern void MyFunction();
}
}
";
await ValidateGeneratedBindings(inputContents, expectedOutputContents, libraryPath: string.Empty);
}
[Theory]
[InlineData("unsigned char value = 0", @"[NativeTypeName(""unsigned char"")] byte value = 0")]
[InlineData("double value = 1.0", @"double value = 1.0")]
@ -195,5 +238,63 @@ namespace ClangSharp.Test
};
await ValidateGeneratedBindings(inputContents, expectedOutputContents, withCallConvs: withCallConvs);
}
[Fact]
public async Task WithSetLastErrorTest()
{
var inputContents = @"void MyFunction1(int value); void MyFunction2(int value);";
var expectedOutputContents = @"using System.Runtime.InteropServices;
namespace ClangSharp.Test
{
public static partial class Methods
{
private const string LibraryPath = ""ClangSharpPInvokeGenerator"";
[DllImport(LibraryPath, CallingConvention = CallingConvention.Cdecl, EntryPoint = ""MyFunction1"", ExactSpelling = true, SetLastError = true)]
public static extern void MyFunction1(int value);
[DllImport(LibraryPath, CallingConvention = CallingConvention.Cdecl, EntryPoint = ""MyFunction2"", ExactSpelling = true)]
public static extern void MyFunction2(int value);
}
}
";
var withSetLastErrors = new string[]
{
"MyFunction1"
};
await ValidateGeneratedBindings(inputContents, expectedOutputContents, withSetLastErrors: withSetLastErrors);
}
[Fact]
public async Task WithSetLastErrorStarTest()
{
var inputContents = @"void MyFunction1(int value); void MyFunction2(int value);";
var expectedOutputContents = @"using System.Runtime.InteropServices;
namespace ClangSharp.Test
{
public static partial class Methods
{
private const string LibraryPath = ""ClangSharpPInvokeGenerator"";
[DllImport(LibraryPath, CallingConvention = CallingConvention.Cdecl, EntryPoint = ""MyFunction1"", ExactSpelling = true, SetLastError = true)]
public static extern void MyFunction1(int value);
[DllImport(LibraryPath, CallingConvention = CallingConvention.Cdecl, EntryPoint = ""MyFunction2"", ExactSpelling = true, SetLastError = true)]
public static extern void MyFunction2(int value);
}
}
";
var withSetLastErrors = new string[]
{
"*"
};
await ValidateGeneratedBindings(inputContents, expectedOutputContents, withSetLastErrors: withSetLastErrors);
}
}
}

Просмотреть файл

@ -0,0 +1,28 @@
// Copyright (c) Microsoft and Contributors. All rights reserved. Licensed under the University of Illinois/NCSA Open Source License. See LICENSE.txt in the project root for license information.
using System.Collections.Generic;
using System.Threading.Tasks;
using Xunit;
namespace ClangSharp.UnitTests
{
public sealed class FunctionPointerDeclarationTest : PInvokeGeneratorTest
{
[Fact]
public async Task BasicTest()
{
var inputContents = @"typedef void (*Callback)();";
var expectedOutputContents = @"using System.Runtime.InteropServices;
namespace ClangSharp.Test
{
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void Callback();
}
";
await ValidateGeneratedBindings(inputContents, expectedOutputContents);
}
}
}

Просмотреть файл

@ -24,17 +24,17 @@ namespace ClangSharp.UnitTests
"-Wno-pragma-once-outside-header" // We are processing files which may be header files
};
protected Task ValidateGeneratedBindings(string inputContents, string expectedOutputContents, string[] excludedNames = null, IReadOnlyDictionary<string, string> remappedNames = null, IReadOnlyDictionary<string, IReadOnlyList<string>> withAttributes = null, IReadOnlyDictionary<string, string> withCallConvs = null, IReadOnlyDictionary<string, string> withTypes = null, IReadOnlyDictionary<string, IReadOnlyList<string>> withUsings = null, IEnumerable<Diagnostic> expectedDiagnostics = null)
protected Task ValidateGeneratedBindings(string inputContents, string expectedOutputContents, string[] excludedNames = null, IReadOnlyDictionary<string, string> remappedNames = null, IReadOnlyDictionary<string, IReadOnlyList<string>> withAttributes = null, IReadOnlyDictionary<string, string> withCallConvs = null, string[] withSetLastErrors = null, IReadOnlyDictionary<string, string> withTypes = null, IReadOnlyDictionary<string, IReadOnlyList<string>> withUsings = null, IEnumerable<Diagnostic> expectedDiagnostics = null, string libraryPath = DefaultLibraryPath)
{
return ValidateGeneratedBindingsAsync(inputContents, expectedOutputContents, PInvokeGeneratorConfigurationOptions.None, excludedNames, remappedNames, withAttributes, withCallConvs, withTypes, withUsings, expectedDiagnostics);
return ValidateGeneratedBindingsAsync(inputContents, expectedOutputContents, PInvokeGeneratorConfigurationOptions.None, excludedNames, remappedNames, withAttributes, withCallConvs, withSetLastErrors, withTypes, withUsings, expectedDiagnostics, libraryPath);
}
protected Task ValidateGeneratedCompatibleBindings(string inputContents, string expectedOutputContents, string[] excludedNames = null, IReadOnlyDictionary<string, string> remappedNames = null, IReadOnlyDictionary<string, IReadOnlyList<string>> withAttributes = null, IReadOnlyDictionary<string, string> withCallConvs = null, IReadOnlyDictionary<string, string> withTypes = null, IReadOnlyDictionary<string, IReadOnlyList<string>> withUsings = null, IEnumerable<Diagnostic> expectedDiagnostics = null)
protected Task ValidateGeneratedCompatibleBindings(string inputContents, string expectedOutputContents, string[] excludedNames = null, IReadOnlyDictionary<string, string> remappedNames = null, IReadOnlyDictionary<string, IReadOnlyList<string>> withAttributes = null, IReadOnlyDictionary<string, string> withCallConvs = null, string[] withSetLastErrors = null, IReadOnlyDictionary<string, string> withTypes = null, IReadOnlyDictionary<string, IReadOnlyList<string>> withUsings = null, IEnumerable<Diagnostic> expectedDiagnostics = null, string libraryPath = DefaultLibraryPath)
{
return ValidateGeneratedBindingsAsync(inputContents, expectedOutputContents, PInvokeGeneratorConfigurationOptions.GenerateCompatibleCode, excludedNames, remappedNames, withAttributes, withCallConvs, withTypes, withUsings, expectedDiagnostics);
return ValidateGeneratedBindingsAsync(inputContents, expectedOutputContents, PInvokeGeneratorConfigurationOptions.GenerateCompatibleCode, excludedNames, remappedNames, withAttributes, withCallConvs, withSetLastErrors, withTypes, withUsings, expectedDiagnostics, libraryPath);
}
private async Task ValidateGeneratedBindingsAsync(string inputContents, string expectedOutputContents, PInvokeGeneratorConfigurationOptions configOptions, string[] excludedNames, IReadOnlyDictionary<string, string> remappedNames, IReadOnlyDictionary<string, IReadOnlyList<string>> withAttributes, IReadOnlyDictionary<string, string> withCallConvs, IReadOnlyDictionary<string, string> withTypes, IReadOnlyDictionary<string, IReadOnlyList<string>> withUsings, IEnumerable<Diagnostic> expectedDiagnostics)
private async Task ValidateGeneratedBindingsAsync(string inputContents, string expectedOutputContents, PInvokeGeneratorConfigurationOptions configOptions, string[] excludedNames, IReadOnlyDictionary<string, string> remappedNames, IReadOnlyDictionary<string, IReadOnlyList<string>> withAttributes, IReadOnlyDictionary<string, string> withCallConvs, string[] withSetLastErrors, IReadOnlyDictionary<string, string> withTypes, IReadOnlyDictionary<string, IReadOnlyList<string>> withUsings, IEnumerable<Diagnostic> expectedDiagnostics, string libraryPath)
{
Assert.True(File.Exists(DefaultInputFileName));
@ -42,7 +42,7 @@ namespace ClangSharp.UnitTests
using var unsavedFile = CXUnsavedFile.Create(DefaultInputFileName, inputContents);
var unsavedFiles = new CXUnsavedFile[] { unsavedFile };
var config = new PInvokeGeneratorConfiguration(DefaultLibraryPath, DefaultNamespaceName, Path.GetRandomFileName(), configOptions, excludedNames, headerFile: null, methodClassName: null, methodPrefixToStrip: null, remappedNames, traversalNames: null, withAttributes, withCallConvs, withTypes, withUsings);
var config = new PInvokeGeneratorConfiguration(libraryPath, DefaultNamespaceName, Path.GetRandomFileName(), configOptions, excludedNames, headerFile: null, methodClassName: null, methodPrefixToStrip: null, remappedNames, traversalNames: null, withAttributes, withCallConvs, withSetLastErrors, withTypes, withUsings);
using (var pinvokeGenerator = new PInvokeGenerator(config, (path) => outputStream))
{

Просмотреть файл

@ -536,6 +536,48 @@ namespace ClangSharp.Test
await ValidateGeneratedBindings(inputContents, expectedOutputContents);
}
[Theory]
[InlineData("double *", "IntPtr")]
[InlineData("short *", "IntPtr")]
[InlineData("int *", "IntPtr")]
[InlineData("float *", "IntPtr")]
public async Task FixedSizedBufferPointerTest(string nativeType, string expectedManagedType)
{
var inputContents = $@"struct MyStruct
{{
{nativeType} c[3];
}};
";
var expectedOutputContents = $@"using System;
using System.Runtime.InteropServices;
namespace ClangSharp.Test
{{
public partial struct MyStruct
{{
[NativeTypeName(""{nativeType}[3]"")]
public _c_e__FixedBuffer c;
public partial struct _c_e__FixedBuffer
{{
internal {expectedManagedType} e0;
internal {expectedManagedType} e1;
internal {expectedManagedType} e2;
public ref {expectedManagedType} this[int index] => ref AsSpan()[index];
public Span<{expectedManagedType}> AsSpan() => MemoryMarshal.CreateSpan(ref e0, 3);
}}
}}
}}
";
await ValidateGeneratedBindings(inputContents, expectedOutputContents);
await ValidateGeneratedBindings(inputContents, expectedOutputContents);
}
[Theory]
[InlineData("unsigned char", "byte")]
[InlineData("double", "double")]