From 27242e84e395161a744f742dca7c0e011067aed6 Mon Sep 17 00:00:00 2001 From: Lucas Girouard-Stranks <519592+lithiumtoast@users.noreply.github.com> Date: Sun, 7 Apr 2024 14:37:42 -0400 Subject: [PATCH] Fix extracting functions with attributes (#22) --- .../functions/function_attributed/config.json | 23 +++++++ .../functions/function_attributed/main.c | 5 ++ ...sonSerializerContextCFfiCrossPlatform.g.cs | 2 +- ...onSerializerContextCFfiTargetPlatform.g.cs | 2 +- .../Domain/Explore/Context/ExploreContext.cs | 65 ++++++++++--------- .../Extract/Domain/Explore/Explorer.cs | 6 +- .../LoggerMessage.g.cs | 4 +- .../Functions/function_attributed/Test.cs | 50 ++++++++++++++ .../Functions/function_int_params_int/Test.cs | 2 +- .../Test.cs | 6 +- .../Functions/function_attributed/Test.cs | 44 +++++++++++++ .../Functions/function_int_params_int/Test.cs | 2 +- .../Test.cs | 6 +- .../Models/CTestFunctionParameter.cs | 4 +- 14 files changed, 175 insertions(+), 46 deletions(-) create mode 100644 src/c/tests/functions/function_attributed/config.json create mode 100644 src/c/tests/functions/function_attributed/main.c create mode 100644 src/cs/tests/c2ffi.Tests.EndToEnd.Extract/Functions/function_attributed/Test.cs create mode 100644 src/cs/tests/c2ffi.Tests.EndToEnd.Merge/Functions/function_attributed/Test.cs diff --git a/src/c/tests/functions/function_attributed/config.json b/src/c/tests/functions/function_attributed/config.json new file mode 100644 index 0000000..b3d8b8e --- /dev/null +++ b/src/c/tests/functions/function_attributed/config.json @@ -0,0 +1,23 @@ +{ + "inputFilePath": "./main.c", + "userIncludeDirectories": [ + "../../../production/ffi_helper/include" + ], + "ignoredIncludeFiles": [ + "../../../production/ffi_helper/include/ffi_helper.h" + ], + "targetPlatforms": { + "windows": { + "x86_64-pc-windows-msvc": {}, + "aarch64-pc-windows-msvc": {} + }, + "macos": { + "aarch64-apple-darwin": {}, + "x86_64-apple-darwin": {}, + }, + "linux": { + "x86_64-unknown-linux-gnu": {}, + "aarch64-unknown-linux-gnu": {} + } + } +} diff --git a/src/c/tests/functions/function_attributed/main.c b/src/c/tests/functions/function_attributed/main.c new file mode 100644 index 0000000..2ac3912 --- /dev/null +++ b/src/c/tests/functions/function_attributed/main.c @@ -0,0 +1,5 @@ +#include +#include "ffi_helper.h" + +extern FFI_API_DECL __attribute__((malloc)) void* __cdecl function_attributed(size_t size); + diff --git a/src/cs/production/c2ffi.Data/Generated/System.Text.Json.SourceGeneration/System.Text.Json.SourceGeneration.JsonSourceGenerator/JsonSerializerContextCFfiCrossPlatform.g.cs b/src/cs/production/c2ffi.Data/Generated/System.Text.Json.SourceGeneration/System.Text.Json.SourceGeneration.JsonSourceGenerator/JsonSerializerContextCFfiCrossPlatform.g.cs index b7977d6..dc32503 100644 --- a/src/cs/production/c2ffi.Data/Generated/System.Text.Json.SourceGeneration/System.Text.Json.SourceGeneration.JsonSourceGenerator/JsonSerializerContextCFfiCrossPlatform.g.cs +++ b/src/cs/production/c2ffi.Data/Generated/System.Text.Json.SourceGeneration/System.Text.Json.SourceGeneration.JsonSourceGenerator/JsonSerializerContextCFfiCrossPlatform.g.cs @@ -8,7 +8,7 @@ namespace c2ffi.Data.Serialization { - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Text.Json.SourceGeneration", "8.0.9.3103")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Text.Json.SourceGeneration", "8.0.9.8001")] public partial class JsonSerializerContextCFfiCrossPlatform { private readonly static global::System.Text.Json.JsonSerializerOptions s_defaultOptions = new() diff --git a/src/cs/production/c2ffi.Data/Generated/System.Text.Json.SourceGeneration/System.Text.Json.SourceGeneration.JsonSourceGenerator/JsonSerializerContextCFfiTargetPlatform.g.cs b/src/cs/production/c2ffi.Data/Generated/System.Text.Json.SourceGeneration/System.Text.Json.SourceGeneration.JsonSourceGenerator/JsonSerializerContextCFfiTargetPlatform.g.cs index bc6da56..cf2f03a 100644 --- a/src/cs/production/c2ffi.Data/Generated/System.Text.Json.SourceGeneration/System.Text.Json.SourceGeneration.JsonSourceGenerator/JsonSerializerContextCFfiTargetPlatform.g.cs +++ b/src/cs/production/c2ffi.Data/Generated/System.Text.Json.SourceGeneration/System.Text.Json.SourceGeneration.JsonSourceGenerator/JsonSerializerContextCFfiTargetPlatform.g.cs @@ -8,7 +8,7 @@ namespace c2ffi.Data.Serialization { - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Text.Json.SourceGeneration", "8.0.9.3103")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Text.Json.SourceGeneration", "8.0.9.8001")] public partial class JsonSerializerContextCFfiTargetPlatform { private readonly static global::System.Text.Json.JsonSerializerOptions s_defaultOptions = new() diff --git a/src/cs/production/c2ffi.Tool/Commands/Extract/Domain/Explore/Context/ExploreContext.cs b/src/cs/production/c2ffi.Tool/Commands/Extract/Domain/Explore/Context/ExploreContext.cs index 2928107..843a5be 100644 --- a/src/cs/production/c2ffi.Tool/Commands/Extract/Domain/Explore/Context/ExploreContext.cs +++ b/src/cs/production/c2ffi.Tool/Commands/Extract/Domain/Explore/Context/ExploreContext.cs @@ -89,41 +89,20 @@ public CTypeInfo VisitType( return string.IsNullOrEmpty(commentString) ? null : commentString; } - public ExploreNodeInfo CreateNodeInfo( + public ExploreNodeInfo CreateTopLevelNodeInfo( CNodeKind nodeKind, clang.CXCursor clangCursor) { - var cursorName = clangCursor.Spelling(); - var cursorType = clang.clang_getCursorType(clangCursor); - return CreateNodeInfo(nodeKind, cursorName, clangCursor, cursorType, null); - } + var clangCursorName = clangCursor.Spelling(); + var clangCursorType = clang.clang_getCursorType(clangCursor); - public ExploreNodeInfo CreateNodeInfo( - CNodeKind kind, - string name, - clang.CXCursor clangCursor, - clang.CXType clangType, - ExploreNodeInfo? parentInfo) - { - var location = ParseContext.Location(clangCursor); - var typeName = clangType.Spelling(); - var sizeOf = ParseContext.SizeOf(kind, clangType); - var alignOf = ParseContext.AlignOf(kind, clangType); - - var result = new ExploreNodeInfo + var clangCursorTypeCanonical = clangCursorType; + if (clangCursorType.kind == clang.CXTypeKind.CXType_Attributed) { - NodeKind = kind, - Name = name, - TypeName = typeName, - Type = clangType, - Cursor = clangCursor, - Location = location, - Parent = parentInfo, - SizeOf = sizeOf, - AlignOf = alignOf - }; + clangCursorTypeCanonical = clang.clang_Type_getModifiedType(clangCursorType); + } - return result; + return CreateNodeInfo(nodeKind, clangCursorName, clangCursor, clangCursorTypeCanonical, null); } public void Dispose() @@ -257,6 +236,34 @@ private CTypeInfo VisitTypeInternal( return typeInfo; } + private ExploreNodeInfo CreateNodeInfo( + CNodeKind kind, + string name, + clang.CXCursor clangCursor, + clang.CXType clangType, + ExploreNodeInfo? parentInfo) + { + var location = ParseContext.Location(clangCursor); + var typeName = clangType.Spelling(); + var sizeOf = ParseContext.SizeOf(kind, clangType); + var alignOf = ParseContext.AlignOf(kind, clangType); + + var result = new ExploreNodeInfo + { + NodeKind = kind, + Name = name, + TypeName = typeName, + Type = clangType, + Cursor = clangCursor, + Location = location, + Parent = parentInfo, + SizeOf = sizeOf, + AlignOf = alignOf + }; + + return result; + } + private static ImmutableDictionary GetNodeHandlers(IServiceProvider services) { var result = new Dictionary diff --git a/src/cs/production/c2ffi.Tool/Commands/Extract/Domain/Explore/Explorer.cs b/src/cs/production/c2ffi.Tool/Commands/Extract/Domain/Explore/Explorer.cs index 06ba26d..8c58541 100644 --- a/src/cs/production/c2ffi.Tool/Commands/Extract/Domain/Explore/Explorer.cs +++ b/src/cs/production/c2ffi.Tool/Commands/Extract/Domain/Explore/Explorer.cs @@ -84,7 +84,7 @@ private void VisitFunctions(ExploreContext context, ParseContext parseContext) private void VisitFunction(ExploreContext context, clang.CXCursor clangCursor) { - var info = context.CreateNodeInfo(CNodeKind.Function, clangCursor); + var info = context.CreateTopLevelNodeInfo(CNodeKind.Function, clangCursor); context.TryEnqueueNode(info); } @@ -99,7 +99,7 @@ private void VisitVariables(ExploreContext context, ParseContext parseContext) private void VisitVariable(ExploreContext context, clang.CXCursor clangCursor) { - var info = context.CreateNodeInfo(CNodeKind.Variable, clangCursor); + var info = context.CreateTopLevelNodeInfo(CNodeKind.Variable, clangCursor); context.TryEnqueueNode(info); } @@ -114,7 +114,7 @@ private void VisitMacroObjects(ExploreContext context, ParseContext parseContext private void VisitMacroObject(ExploreContext context, clang.CXCursor clangCursor) { - var info = context.CreateNodeInfo(CNodeKind.MacroObject, clangCursor); + var info = context.CreateTopLevelNodeInfo(CNodeKind.MacroObject, clangCursor); context.TryEnqueueNode(info); } diff --git a/src/cs/production/c2ffi.Tool/Generated/Microsoft.Extensions.Logging.Generators/Microsoft.Extensions.Logging.Generators.LoggerMessageGenerator/LoggerMessage.g.cs b/src/cs/production/c2ffi.Tool/Generated/Microsoft.Extensions.Logging.Generators/Microsoft.Extensions.Logging.Generators.LoggerMessageGenerator/LoggerMessage.g.cs index eeee4de..5fd91e9 100644 --- a/src/cs/production/c2ffi.Tool/Generated/Microsoft.Extensions.Logging.Generators/Microsoft.Extensions.Logging.Generators.LoggerMessageGenerator/LoggerMessage.g.cs +++ b/src/cs/production/c2ffi.Tool/Generated/Microsoft.Extensions.Logging.Generators/Microsoft.Extensions.Logging.Generators.LoggerMessageGenerator/LoggerMessage.g.cs @@ -1,5 +1,5 @@ -// -#nullable enable +// +#nullable enable namespace c2ffi.Tool.Commands.Extract.Domain.Explore.Context { diff --git a/src/cs/tests/c2ffi.Tests.EndToEnd.Extract/Functions/function_attributed/Test.cs b/src/cs/tests/c2ffi.Tests.EndToEnd.Extract/Functions/function_attributed/Test.cs new file mode 100644 index 0000000..2062045 --- /dev/null +++ b/src/cs/tests/c2ffi.Tests.EndToEnd.Extract/Functions/function_attributed/Test.cs @@ -0,0 +1,50 @@ +// Copyright (c) Bottlenose Labs Inc. (https://github.com/bottlenoselabs). All rights reserved. +// Licensed under the MIT license. See LICENSE file in the Git repository root directory for full license information. + +using c2ffi.Tests.Library.Models; +using FluentAssertions; + +#pragma warning disable CA1707 + +namespace c2ffi.Tests.EndToEnd.Extract.Functions.function_attributed; + +public class Test : ExtractFfiTest +{ + private const string FunctionName = "function_attributed"; + + [Fact] + public void Function() + { + var ffis = GetTargetPlatformFfis( + $"src/c/tests/functions/{FunctionName}/config.json"); + Assert.True(ffis.Length > 0); + + foreach (var ffi in ffis) + { + FfiFunctionExists(ffi); + } + } + + private void FfiFunctionExists(CTestFfiTargetPlatform ffi) + { + var function = ffi.GetFunction(FunctionName); + function.CallingConvention.Should().Be("cdecl"); + + var returnType = function.ReturnType; + returnType.Name.Should().Be("void *"); + returnType.SizeOf.Should().Be(ffi.PointerSize); + returnType.AlignOf.Should().Be(ffi.PointerSize); + returnType.NodeKind.Should().Be("pointer"); + returnType.InnerType.Should().NotBeNull(); + returnType.InnerType!.Name.Should().Be("void"); + returnType.InnerType!.NodeKind.Should().Be("primitive"); + returnType.InnerType.InnerType.Should().BeNull(); + + function.Parameters.Should().NotBeEmpty(); + function.Parameters.Length.Should().Be(1); + var parameter = function.Parameters[0]; + parameter.Name.Should().Be("size"); + parameter.Type.Name.Should().Be("size_t"); + parameter.Type.InnerType.Should().NotBeNull(); + } +} diff --git a/src/cs/tests/c2ffi.Tests.EndToEnd.Extract/Functions/function_int_params_int/Test.cs b/src/cs/tests/c2ffi.Tests.EndToEnd.Extract/Functions/function_int_params_int/Test.cs index c5bcff7..5f119b5 100644 --- a/src/cs/tests/c2ffi.Tests.EndToEnd.Extract/Functions/function_int_params_int/Test.cs +++ b/src/cs/tests/c2ffi.Tests.EndToEnd.Extract/Functions/function_int_params_int/Test.cs @@ -40,6 +40,6 @@ private static void FfiFunctionExists(CTestFfiTargetPlatform ffi) function.Parameters.Length.Should().Be(1); var parameter = function.Parameters[0]; parameter.Name.Should().Be("a"); - parameter.TypeName.Should().Be("int"); + parameter.Type.Name.Should().Be("int"); } } diff --git a/src/cs/tests/c2ffi.Tests.EndToEnd.Extract/Functions/function_uint64_params_uint8_uint16_uint32/Test.cs b/src/cs/tests/c2ffi.Tests.EndToEnd.Extract/Functions/function_uint64_params_uint8_uint16_uint32/Test.cs index b9199a5..5596c88 100644 --- a/src/cs/tests/c2ffi.Tests.EndToEnd.Extract/Functions/function_uint64_params_uint8_uint16_uint32/Test.cs +++ b/src/cs/tests/c2ffi.Tests.EndToEnd.Extract/Functions/function_uint64_params_uint8_uint16_uint32/Test.cs @@ -41,14 +41,14 @@ private static void FfiFunctionExists(CTestFfiTargetPlatform ffi) var parameter1 = function.Parameters[0]; parameter1.Name.Should().Be("a"); - parameter1.TypeName.Should().Be("uint8_t"); + parameter1.Type.Name.Should().Be("uint8_t"); var parameter2 = function.Parameters[1]; parameter2.Name.Should().Be("b"); - parameter2.TypeName.Should().Be("uint16_t"); + parameter2.Type.Name.Should().Be("uint16_t"); var parameter3 = function.Parameters[2]; parameter3.Name.Should().Be("c"); - parameter3.TypeName.Should().Be("uint32_t"); + parameter3.Type.Name.Should().Be("uint32_t"); } } diff --git a/src/cs/tests/c2ffi.Tests.EndToEnd.Merge/Functions/function_attributed/Test.cs b/src/cs/tests/c2ffi.Tests.EndToEnd.Merge/Functions/function_attributed/Test.cs new file mode 100644 index 0000000..21f3c7a --- /dev/null +++ b/src/cs/tests/c2ffi.Tests.EndToEnd.Merge/Functions/function_attributed/Test.cs @@ -0,0 +1,44 @@ +// Copyright (c) Bottlenose Labs Inc. (https://github.com/bottlenoselabs). All rights reserved. +// Licensed under the MIT license. See LICENSE file in the Git repository root directory for full license information. + +using c2ffi.Tests.Library.Models; +using FluentAssertions; +using Xunit; + +#pragma warning disable CA1707 + +namespace c2ffi.Tests.EndToEnd.Merge.Functions.function_attributed; + +public class Test : MergeFfisTest +{ + private const string FunctionName = "function_attributed"; + + [Fact] + public void Function() + { + var ffi = GetCrossPlatformFfi( + $"src/c/tests/functions/{FunctionName}/ffi"); + FfiFunctionExists(ffi); + } + + private void FfiFunctionExists(CTestFfiCrossPlatform ffi) + { + var function = ffi.GetFunction(FunctionName); + function.CallingConvention.Should().Be("cdecl"); + + var returnType = function.ReturnType; + returnType.Name.Should().Be("void *"); + returnType.NodeKind.Should().Be("pointer"); + returnType.InnerType.Should().NotBeNull(); + returnType.InnerType!.Name.Should().Be("void"); + returnType.InnerType!.NodeKind.Should().Be("primitive"); + returnType.InnerType.InnerType.Should().BeNull(); + + function.Parameters.Should().NotBeEmpty(); + function.Parameters.Length.Should().Be(1); + var parameter = function.Parameters[0]; + parameter.Name.Should().Be("size"); + parameter.Type.Name.Should().Be("size_t"); + parameter.Type.InnerType.Should().NotBeNull(); + } +} diff --git a/src/cs/tests/c2ffi.Tests.EndToEnd.Merge/Functions/function_int_params_int/Test.cs b/src/cs/tests/c2ffi.Tests.EndToEnd.Merge/Functions/function_int_params_int/Test.cs index a476b96..e0e2702 100644 --- a/src/cs/tests/c2ffi.Tests.EndToEnd.Merge/Functions/function_int_params_int/Test.cs +++ b/src/cs/tests/c2ffi.Tests.EndToEnd.Merge/Functions/function_int_params_int/Test.cs @@ -37,6 +37,6 @@ private void FfiFunctionExists(CTestFfiCrossPlatform ffi) function.Parameters.Length.Should().Be(1); var parameter = function.Parameters[0]; parameter.Name.Should().Be("a"); - parameter.TypeName.Should().Be("int"); + parameter.Type.Name.Should().Be("int"); } } diff --git a/src/cs/tests/c2ffi.Tests.EndToEnd.Merge/Functions/function_uint64_params_uint8_uint16_uint32/Test.cs b/src/cs/tests/c2ffi.Tests.EndToEnd.Merge/Functions/function_uint64_params_uint8_uint16_uint32/Test.cs index b55d069..601653f 100644 --- a/src/cs/tests/c2ffi.Tests.EndToEnd.Merge/Functions/function_uint64_params_uint8_uint16_uint32/Test.cs +++ b/src/cs/tests/c2ffi.Tests.EndToEnd.Merge/Functions/function_uint64_params_uint8_uint16_uint32/Test.cs @@ -38,14 +38,14 @@ private static void FfiFunctionExists(CTestFfiCrossPlatform ffi) var parameter1 = function.Parameters[0]; parameter1.Name.Should().Be("a"); - parameter1.TypeName.Should().Be("uint8_t"); + parameter1.Type.Name.Should().Be("uint8_t"); var parameter2 = function.Parameters[1]; parameter2.Name.Should().Be("b"); - parameter2.TypeName.Should().Be("uint16_t"); + parameter2.Type.Name.Should().Be("uint16_t"); var parameter3 = function.Parameters[2]; parameter3.Name.Should().Be("c"); - parameter3.TypeName.Should().Be("uint32_t"); + parameter3.Type.Name.Should().Be("uint32_t"); } } diff --git a/src/cs/tests/c2ffi.Tests.Library/Models/CTestFunctionParameter.cs b/src/cs/tests/c2ffi.Tests.Library/Models/CTestFunctionParameter.cs index 278e01d..8b2344f 100644 --- a/src/cs/tests/c2ffi.Tests.Library/Models/CTestFunctionParameter.cs +++ b/src/cs/tests/c2ffi.Tests.Library/Models/CTestFunctionParameter.cs @@ -13,12 +13,12 @@ public class CTestFunctionParameter { public string Name { get; } - public string TypeName { get; } + public CTestTypeInfo Type { get; } public CTestFunctionParameter(CFunctionParameter functionParameter) { Name = functionParameter.Name; - TypeName = functionParameter.TypeInfo.Name; + Type = new CTestTypeInfo(functionParameter.TypeInfo); } public override string ToString()