diff --git a/Bindgen.NET/BindingGenerator.cs b/Bindgen.NET/BindingGenerator.cs index a2be220..38e6acd 100644 --- a/Bindgen.NET/BindingGenerator.cs +++ b/Bindgen.NET/BindingGenerator.cs @@ -1,4 +1,4 @@ -using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Text; using ClangSharp; @@ -112,11 +112,11 @@ public static string Generate(BindingOptions options) using System; using System.Runtime.InteropServices; using System.Runtime.CompilerServices; - + {{(Options.GenerateDisableRuntimeMarshallingAttribute ? "[assembly: DisableRuntimeMarshalling]" : "")}} - + namespace {{Options.Namespace}}; - + public static unsafe partial class {{Options.Class}} { {{GenerateBindgenInternal()}} @@ -439,7 +439,7 @@ private static string GetTypeName(Type? type) typeName = GetTypeName(autoType.GetDeducedType); if (type is ConstantArrayType constantArrayType) - typeName = $"{GetTypeName(constantArrayType.ElementType)}_{constantArrayType.Size}"; + typeName = $"{GetTypeIdentifier(constantArrayType.ElementType)}_{constantArrayType.Size}"; if (type is FunctionProtoType functionProtoType) typeName = GetCSharpFunctionPointer(functionProtoType); @@ -664,23 +664,24 @@ private static string GenerateFunctionDecl(FunctionDecl functionDecl) private static string GenerateConstantArrayType(ConstantArrayType type) { - string name = GetTypeName(type.ElementType); + string elementTypeName = GetTypeName(type.ElementType); + string structName = GetTypeName(type); return GenerateOuterDeclarations(type, $$""" [InlineArray({{type.Size}})] - public partial struct {{name}}_{{type.Size}} + public partial struct {{structName}} { - public {{name}} Item0; + public {{elementTypeName}} Item0; } """); } private static string GenerateConstantArrayTypeEqualityMethods(ConstantArrayType type) { - string name = GetTypeName(type); + string structIdentifier = GetTypeName(type); return GenerateOuterDeclarations(type, $$""" - public partial struct {{name}} : IEquatable<{{name}}> + public partial struct {{structIdentifier}} : IEquatable<{{structIdentifier}}> { - {{GenerateRecordEqualityFunctions(name)}} + {{GenerateRecordEqualityFunctions(structIdentifier)}} } """); } @@ -762,7 +763,7 @@ private static string GenerateEnumDecl(EnumDecl enumDecl) return GenerateOuterDeclarations(enumDecl.TypeForDecl, $@" public enum {GetTypeName(enumDecl.TypeForDecl)} : {GetIntegerName(enumDecl.IntegerType.Handle.SizeOf, hasNegatives, "INVALID_ENUM_INTEGER")} - {{ + {{ {string.Join(",\n", enumMembers)} }} "); @@ -914,6 +915,78 @@ private static string GenerateMacroDummy(MacroDefinitionRecord macro) return $"const __auto_type {MacroPrefix}{macro.Name} = {value};"; } + /// + /// Returns a valid C# identifier name for the given type. + /// Unlike , this always returns a name that can + /// be used as a struct/class identifier (no special characters like *, <, >). + /// + private static string GetTypeIdentifier(Type type) + { + string name = GetTypeName(type); + + // Check if the name is already a valid C# identifier + if (IsValidIdentifier(name)) + return name; + + // For function pointer types, generate a deterministic hash-based name + if (type is FunctionProtoType fpt) + { + string sig = GetCSharpFunctionPointer(fpt); + int hash = GetDeterministicHashCode(sig); + return $"_FunctionPtr_{hash:X8}"; + } + + if (type is PointerType ptrType && ptrType.CanonicalType.PointeeType is FunctionProtoType) + { + string sig = GetCSharpFunctionPointer((FunctionProtoType)ptrType.CanonicalType.PointeeType); + int hash = GetDeterministicHashCode(sig); + return $"_FunctionPtr_{hash:X8}"; + } + + // For pointer/reference types, replace special chars for identifier safety + if (name.Contains('*') || name.Contains('<') || name.Contains('>') || name.Contains(',')) + { + return name + .Replace('*', 'P') + .Replace('<', '_') + .Replace('>', '_') + .Replace(',', '_') + .Replace(' ', '_') + .Replace('.', '_'); + } + + // Fallback: strip all non-identifier characters + return string.Concat(name.Where(c => char.IsLetterOrDigit(c) || c == '_')); + } + + private static bool IsValidIdentifier(string name) + { + if (string.IsNullOrEmpty(name)) + return false; + + if (!char.IsLetter(name[0]) && name[0] != '_') + return false; + + for (int i = 1; i < name.Length; i++) + { + if (!char.IsLetterOrDigit(name[i]) && name[i] != '_') + return false; + } + + return true; + } + + private static int GetDeterministicHashCode(string str) + { + unchecked + { + int hash = 17; + foreach (char c in str) + hash = hash * 31 + c; + return hash; + } + } + private static string GenerateRecordEqualityFunctions(string recordName) { return $$"""