From 2a6a54977d64a2afaa86253611083ca983c4a219 Mon Sep 17 00:00:00 2001 From: Lucas Girouard-Stranks <519592+lithiumtoast@users.noreply.github.com> Date: Tue, 7 Jan 2025 19:36:53 -0500 Subject: [PATCH] Generate nested struct if anonymous struct or union has a field name (#172) * Global using * Refactor `BaseGenerator` * Refactor * Don't generate node if it's using a C# built in type name * Generate nested struct if anonymous struct or union has a field name * Use parent struct name in nested struct --- .../BuildCLibrary/CMakeLibraryBuilder.cs | 1 - .../c2cs.Tool/BuildCLibrary/Command.cs | 1 - .../c2cs.Tool/BuildCLibrary/InputSanitizer.cs | 1 - .../production/c2cs.Tool/CommandLineHost.cs | 1 - .../GenerateCSharpCode/CodeGenerator.cs | 5 ++-- .../CodeGeneratorContext.cs | 14 +++++++-- .../CodeGeneratorDocumentInteropRuntime.cs | 1 - .../CodeGeneratorDocumentOptions.cs | 1 - .../CodeGeneratorDocumentPInvoke.cs | 1 - .../c2cs.Tool/GenerateCSharpCode/Command.cs | 1 - .../Generators/BaseGenerator.cs | 23 ++------------ .../Generators/GeneratorFunction.cs | 1 - .../Generators/GeneratorFunctionPointer.cs | 6 ++-- .../Generators/GeneratorStruct.cs | 30 +++++++++++++++---- .../GenerateCSharpCode/InputSanitizer.cs | 1 - .../GenerateCSharpCode/NameMapper.cs | 1 - .../c2cs.Tool/GenerateCSharpCode/Startup.cs | 3 +- .../c2cs.Tool/GenerateCSharpCode/Tool.cs | 1 - src/cs/production/c2cs.Tool/GlobalUsings.cs | 4 +++ src/cs/production/c2cs.Tool/Program.cs | 1 - src/cs/production/c2cs.Tool/Startup.cs | 1 - 21 files changed, 49 insertions(+), 50 deletions(-) create mode 100644 src/cs/production/c2cs.Tool/GlobalUsings.cs diff --git a/src/cs/production/c2cs.Tool/BuildCLibrary/CMakeLibraryBuilder.cs b/src/cs/production/c2cs.Tool/BuildCLibrary/CMakeLibraryBuilder.cs index b5d0925c..a5f2733e 100644 --- a/src/cs/production/c2cs.Tool/BuildCLibrary/CMakeLibraryBuilder.cs +++ b/src/cs/production/c2cs.Tool/BuildCLibrary/CMakeLibraryBuilder.cs @@ -1,7 +1,6 @@ // 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 System; using System.Collections.Generic; using System.Collections.Immutable; using System.IO; diff --git a/src/cs/production/c2cs.Tool/BuildCLibrary/Command.cs b/src/cs/production/c2cs.Tool/BuildCLibrary/Command.cs index 5f56166f..ba256738 100644 --- a/src/cs/production/c2cs.Tool/BuildCLibrary/Command.cs +++ b/src/cs/production/c2cs.Tool/BuildCLibrary/Command.cs @@ -1,7 +1,6 @@ // 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 System; using System.Collections.Immutable; using System.CommandLine; using System.Linq; diff --git a/src/cs/production/c2cs.Tool/BuildCLibrary/InputSanitizer.cs b/src/cs/production/c2cs.Tool/BuildCLibrary/InputSanitizer.cs index a82859e0..380e9dbf 100644 --- a/src/cs/production/c2cs.Tool/BuildCLibrary/InputSanitizer.cs +++ b/src/cs/production/c2cs.Tool/BuildCLibrary/InputSanitizer.cs @@ -1,7 +1,6 @@ // 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 System; using System.Collections.Immutable; using System.IO.Abstractions; using System.Linq; diff --git a/src/cs/production/c2cs.Tool/CommandLineHost.cs b/src/cs/production/c2cs.Tool/CommandLineHost.cs index d2d21a27..7f10d1b8 100644 --- a/src/cs/production/c2cs.Tool/CommandLineHost.cs +++ b/src/cs/production/c2cs.Tool/CommandLineHost.cs @@ -1,7 +1,6 @@ // 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 System; using System.CommandLine; using System.Threading; using System.Threading.Tasks; diff --git a/src/cs/production/c2cs.Tool/GenerateCSharpCode/CodeGenerator.cs b/src/cs/production/c2cs.Tool/GenerateCSharpCode/CodeGenerator.cs index 179f144a..2fd0b9c7 100644 --- a/src/cs/production/c2cs.Tool/GenerateCSharpCode/CodeGenerator.cs +++ b/src/cs/production/c2cs.Tool/GenerateCSharpCode/CodeGenerator.cs @@ -1,7 +1,6 @@ // 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 System; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; @@ -27,7 +26,7 @@ public sealed partial class CodeGenerator private readonly CodeGeneratorDocumentInteropRuntime _codeGeneratorDocumentInteropRuntime; private readonly InputSanitized _input; - private readonly ImmutableDictionary _nodeCodeGenerators; + private readonly ImmutableDictionary _nodeCodeGenerators; public CodeGenerator( IServiceProvider services, @@ -39,7 +38,7 @@ public CodeGenerator( _codeGeneratorDocumentAssemblyAttributes = services.GetRequiredService(); _codeGeneratorDocumentInteropRuntime = services.GetRequiredService(); - _nodeCodeGenerators = new Dictionary + _nodeCodeGenerators = new Dictionary { { typeof(CEnum), services.GetRequiredService() }, { typeof(CFunction), services.GetRequiredService() }, diff --git a/src/cs/production/c2cs.Tool/GenerateCSharpCode/CodeGeneratorContext.cs b/src/cs/production/c2cs.Tool/GenerateCSharpCode/CodeGeneratorContext.cs index bdc8977f..ae9252ab 100644 --- a/src/cs/production/c2cs.Tool/GenerateCSharpCode/CodeGeneratorContext.cs +++ b/src/cs/production/c2cs.Tool/GenerateCSharpCode/CodeGeneratorContext.cs @@ -1,7 +1,6 @@ // 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 System; using System.Collections.Generic; using System.Collections.Immutable; using bottlenoselabs.Common.Tools; @@ -19,7 +18,7 @@ public sealed class CodeGeneratorContext #pragma warning disable IDE0032 private readonly NameMapper _nameMapper; #pragma warning restore IDE0032 - private readonly ImmutableDictionary _nodeCodeGenerators; + private readonly ImmutableDictionary _nodeCodeGenerators; private readonly HashSet _existingNamesCSharp = []; public InputSanitized Input { get; } @@ -31,7 +30,7 @@ public sealed class CodeGeneratorContext public CodeGeneratorContext( InputSanitized input, CFfiCrossPlatform ffi, - ImmutableDictionary nodeCodeGenerators) + ImmutableDictionary nodeCodeGenerators) { Input = input; Ffi = ffi; @@ -46,6 +45,15 @@ public CodeGeneratorContext( var codeGenerator = GetCodeGenerator(); var nameCSharp = _nameMapper.GetNodeNameCSharp(node); + var nameCSharpCollidesWithBuiltInName = nameCSharp + // https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/built-in-types + is "bool" or "byte" or "sbyte" or "char" or "decimal" or "double" or "float" or "int" or "uint" or "nint" + or "nuint" or "long" or "ulong" or "short" or "ushort" or "object" or "string" or "dynamic"; + if (nameCSharpCollidesWithBuiltInName) + { + return null; + } + var isAlreadyAdded = !_existingNamesCSharp.Add(nameCSharp); if (isAlreadyAdded) { diff --git a/src/cs/production/c2cs.Tool/GenerateCSharpCode/CodeGeneratorDocumentInteropRuntime.cs b/src/cs/production/c2cs.Tool/GenerateCSharpCode/CodeGeneratorDocumentInteropRuntime.cs index 5dd875ab..ce57a1f3 100644 --- a/src/cs/production/c2cs.Tool/GenerateCSharpCode/CodeGeneratorDocumentInteropRuntime.cs +++ b/src/cs/production/c2cs.Tool/GenerateCSharpCode/CodeGeneratorDocumentInteropRuntime.cs @@ -1,7 +1,6 @@ // 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 System; using System.Collections.Immutable; using System.IO; using System.Reflection; diff --git a/src/cs/production/c2cs.Tool/GenerateCSharpCode/CodeGeneratorDocumentOptions.cs b/src/cs/production/c2cs.Tool/GenerateCSharpCode/CodeGeneratorDocumentOptions.cs index 6ba1d4cc..ab0338e9 100644 --- a/src/cs/production/c2cs.Tool/GenerateCSharpCode/CodeGeneratorDocumentOptions.cs +++ b/src/cs/production/c2cs.Tool/GenerateCSharpCode/CodeGeneratorDocumentOptions.cs @@ -1,7 +1,6 @@ // 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 System; using System.Globalization; using System.Reflection; diff --git a/src/cs/production/c2cs.Tool/GenerateCSharpCode/CodeGeneratorDocumentPInvoke.cs b/src/cs/production/c2cs.Tool/GenerateCSharpCode/CodeGeneratorDocumentPInvoke.cs index 6805e735..e9d62b70 100644 --- a/src/cs/production/c2cs.Tool/GenerateCSharpCode/CodeGeneratorDocumentPInvoke.cs +++ b/src/cs/production/c2cs.Tool/GenerateCSharpCode/CodeGeneratorDocumentPInvoke.cs @@ -1,7 +1,6 @@ // 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 System; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; diff --git a/src/cs/production/c2cs.Tool/GenerateCSharpCode/Command.cs b/src/cs/production/c2cs.Tool/GenerateCSharpCode/Command.cs index 77b088b6..734c66e0 100644 --- a/src/cs/production/c2cs.Tool/GenerateCSharpCode/Command.cs +++ b/src/cs/production/c2cs.Tool/GenerateCSharpCode/Command.cs @@ -1,7 +1,6 @@ // 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 System; using System.CommandLine; using JetBrains.Annotations; diff --git a/src/cs/production/c2cs.Tool/GenerateCSharpCode/Generators/BaseGenerator.cs b/src/cs/production/c2cs.Tool/GenerateCSharpCode/Generators/BaseGenerator.cs index 6e22fdbb..6458d606 100644 --- a/src/cs/production/c2cs.Tool/GenerateCSharpCode/Generators/BaseGenerator.cs +++ b/src/cs/production/c2cs.Tool/GenerateCSharpCode/Generators/BaseGenerator.cs @@ -6,29 +6,10 @@ namespace C2CS.GenerateCSharpCode.Generators; -public abstract class BaseGenerator(ILogger logger) -{ - protected readonly ILogger Logger = logger; - - protected abstract string? GenerateCode( - string nameCSharp, - CodeGeneratorContext context, - object obj); -} - public abstract class BaseGenerator(ILogger> logger) - : BaseGenerator(logger) where TNode : CNode { - public abstract string? GenerateCode(CodeGeneratorContext context, string nameCSharp, TNode node); + protected readonly ILogger> Logger = logger; - protected override string? GenerateCode( - string nameCSharp, - CodeGeneratorContext context, - object obj) - { - var node = (TNode)obj; - var code = GenerateCode(context, nameCSharp, node); - return code; - } + public abstract string? GenerateCode(CodeGeneratorContext context, string nameCSharp, TNode node); } diff --git a/src/cs/production/c2cs.Tool/GenerateCSharpCode/Generators/GeneratorFunction.cs b/src/cs/production/c2cs.Tool/GenerateCSharpCode/Generators/GeneratorFunction.cs index dcf4d9e5..0e20060d 100644 --- a/src/cs/production/c2cs.Tool/GenerateCSharpCode/Generators/GeneratorFunction.cs +++ b/src/cs/production/c2cs.Tool/GenerateCSharpCode/Generators/GeneratorFunction.cs @@ -1,7 +1,6 @@ // 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 System; using System.Collections.Generic; using System.Linq; using c2ffi.Data; diff --git a/src/cs/production/c2cs.Tool/GenerateCSharpCode/Generators/GeneratorFunctionPointer.cs b/src/cs/production/c2cs.Tool/GenerateCSharpCode/Generators/GeneratorFunctionPointer.cs index d03e825d..4574cb59 100644 --- a/src/cs/production/c2cs.Tool/GenerateCSharpCode/Generators/GeneratorFunctionPointer.cs +++ b/src/cs/production/c2cs.Tool/GenerateCSharpCode/Generators/GeneratorFunctionPointer.cs @@ -23,18 +23,18 @@ public override string GenerateCode(CodeGeneratorContext context, string nameCSh #pragma warning disable SA1202 internal static string GenerateCodeFunctionPointer( - NameMapper nameMapper, string name, CFunctionPointer node) + NameMapper nameMapper, string nameCSharp, CFunctionPointer node) #pragma warning restore SA1202 { var functionPointerTypeNameCSharp = GetFunctionPointerTypeNameCSharp(nameMapper, node); var code = $$""" [StructLayout(LayoutKind.Sequential)] - public partial struct {{name}} + public partial struct {{nameCSharp}} { public {{functionPointerTypeNameCSharp}} Pointer; - public {{name}}({{functionPointerTypeNameCSharp}} pointer) + public {{nameCSharp}}({{functionPointerTypeNameCSharp}} pointer) { Pointer = pointer; } diff --git a/src/cs/production/c2cs.Tool/GenerateCSharpCode/Generators/GeneratorStruct.cs b/src/cs/production/c2cs.Tool/GenerateCSharpCode/Generators/GeneratorStruct.cs index eb30c49a..cab1d993 100644 --- a/src/cs/production/c2cs.Tool/GenerateCSharpCode/Generators/GeneratorStruct.cs +++ b/src/cs/production/c2cs.Tool/GenerateCSharpCode/Generators/GeneratorStruct.cs @@ -2,6 +2,7 @@ // Licensed under the MIT license. See LICENSE file in the Git repository root directory for full license information. using System.Collections.Immutable; +using System.Linq; using c2ffi.Data.Nodes; using JetBrains.Annotations; using Microsoft.Extensions.Logging; @@ -15,7 +16,7 @@ public class GeneratorStruct(ILogger logger) public override string GenerateCode(CodeGeneratorContext context, string nameCSharp, CRecord record) { var codeStructMembers = GenerateCodeStructMembers( - context, record.Fields, record.NestedRecords, 0); + context, nameCSharp, record.Fields, record.NestedRecords, 0); var membersCode = string.Join("\n\n", codeStructMembers); var code = $$""" @@ -31,11 +32,13 @@ public partial struct {{nameCSharp}} private ImmutableArray GenerateCodeStructMembers( CodeGeneratorContext context, + string structNameCSharp, ImmutableArray fields, ImmutableArray nestedStructs, int parentFieldOffsetOf) { var codeFields = ImmutableArray.CreateBuilder(); + var codeNestedStructs = ImmutableArray.CreateBuilder(); var nestedStructIndex = 0; for (var i = 0; i < fields.Length; i++) @@ -46,9 +49,25 @@ private ImmutableArray GenerateCodeStructMembers( if (field.Type.IsAnonymous ?? false) { var nestedStruct = nestedStructs[nestedStructIndex++]; - var codeNestedStructMembers = GenerateCodeStructMembers( - context, nestedStruct.Fields, nestedStruct.NestedRecords, fieldOffsetOf); - codeFields.AddRange(codeNestedStructMembers); + var fieldNameCSharp = context.NameMapper.GetIdentifierCSharp(field.Name); + if (string.IsNullOrEmpty(fieldNameCSharp)) + { + var codeNestedStructMembers = GenerateCodeStructMembers( + context, structNameCSharp, nestedStruct.Fields, nestedStruct.NestedRecords, fieldOffsetOf); + codeFields.AddRange(codeNestedStructMembers); + } + else + { + var fieldNameCSharpParts = fieldNameCSharp.Split('_', StringSplitOptions.RemoveEmptyEntries) + .Select(x => $"{x[0].ToString().ToUpperInvariant()}{x.AsSpan(1)}"); + var nestedStructName = $"{structNameCSharp}_{string.Join('_', fieldNameCSharpParts)}"; + var codeNestedStruct = GenerateCode(context, nestedStructName, nestedStruct); + codeNestedStructs.Add(codeNestedStruct); + + var codeField = GenerateCodeStructField( + context.NameMapper, field, fieldNameCSharp, nestedStructName, fieldOffsetOf); + codeFields.Add(codeField); + } } else { @@ -60,7 +79,8 @@ private ImmutableArray GenerateCodeStructMembers( } } - return codeFields.ToImmutable(); + var result = codeFields.ToImmutable().AddRange(codeNestedStructs.ToImmutable()); + return result; } private string GenerateCodeStructField( diff --git a/src/cs/production/c2cs.Tool/GenerateCSharpCode/InputSanitizer.cs b/src/cs/production/c2cs.Tool/GenerateCSharpCode/InputSanitizer.cs index 377ee899..50293afd 100644 --- a/src/cs/production/c2cs.Tool/GenerateCSharpCode/InputSanitizer.cs +++ b/src/cs/production/c2cs.Tool/GenerateCSharpCode/InputSanitizer.cs @@ -1,7 +1,6 @@ // 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 System; using System.Collections.Generic; using System.Collections.Immutable; using System.IO; diff --git a/src/cs/production/c2cs.Tool/GenerateCSharpCode/NameMapper.cs b/src/cs/production/c2cs.Tool/GenerateCSharpCode/NameMapper.cs index b6f9e764..d52d0342 100644 --- a/src/cs/production/c2cs.Tool/GenerateCSharpCode/NameMapper.cs +++ b/src/cs/production/c2cs.Tool/GenerateCSharpCode/NameMapper.cs @@ -1,7 +1,6 @@ // 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 System; using System.Collections.Generic; using System.Globalization; using System.Linq; diff --git a/src/cs/production/c2cs.Tool/GenerateCSharpCode/Startup.cs b/src/cs/production/c2cs.Tool/GenerateCSharpCode/Startup.cs index d871980c..26d8f1bf 100644 --- a/src/cs/production/c2cs.Tool/GenerateCSharpCode/Startup.cs +++ b/src/cs/production/c2cs.Tool/GenerateCSharpCode/Startup.cs @@ -27,7 +27,8 @@ public static void ConfigureServices(IServiceCollection services) private static void AddNodeCodeGenerators(IServiceCollection services) { var assembly = Assembly.GetExecutingAssembly(); - var types = assembly.GetTypes().Where(p => !p.IsAbstract && typeof(BaseGenerator).IsAssignableFrom(p)) + var types = assembly.GetTypes().Where(t => + t is { IsAbstract: false, IsInterface: false, IsGenericType: false, BaseType.IsGenericType: true } && t.BaseType.GetGenericTypeDefinition() == typeof(BaseGenerator<>)) .ToImmutableArray(); foreach (var type in types) { diff --git a/src/cs/production/c2cs.Tool/GenerateCSharpCode/Tool.cs b/src/cs/production/c2cs.Tool/GenerateCSharpCode/Tool.cs index 38f01f11..19ae79ad 100644 --- a/src/cs/production/c2cs.Tool/GenerateCSharpCode/Tool.cs +++ b/src/cs/production/c2cs.Tool/GenerateCSharpCode/Tool.cs @@ -1,7 +1,6 @@ // 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 System; using System.IO; using System.IO.Abstractions; using bottlenoselabs.Common.Tools; diff --git a/src/cs/production/c2cs.Tool/GlobalUsings.cs b/src/cs/production/c2cs.Tool/GlobalUsings.cs new file mode 100644 index 00000000..5ccf782b --- /dev/null +++ b/src/cs/production/c2cs.Tool/GlobalUsings.cs @@ -0,0 +1,4 @@ +// 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. + +global using System; diff --git a/src/cs/production/c2cs.Tool/Program.cs b/src/cs/production/c2cs.Tool/Program.cs index 7c726bad..9ea05d2e 100644 --- a/src/cs/production/c2cs.Tool/Program.cs +++ b/src/cs/production/c2cs.Tool/Program.cs @@ -1,7 +1,6 @@ // 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 System; using Microsoft.Extensions.Hosting; namespace C2CS; diff --git a/src/cs/production/c2cs.Tool/Startup.cs b/src/cs/production/c2cs.Tool/Startup.cs index d3515a33..730b3894 100644 --- a/src/cs/production/c2cs.Tool/Startup.cs +++ b/src/cs/production/c2cs.Tool/Startup.cs @@ -1,7 +1,6 @@ // 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 System; using System.Collections.Immutable; using System.IO; using System.IO.Abstractions;