From d4ee8c4600acf977f1204bd58f70c1cfa51cc15c Mon Sep 17 00:00:00 2001 From: William Chen Date: Sun, 7 Jun 2026 19:51:03 -0400 Subject: [PATCH 01/37] Start of generator fixes and polish branch From 4e390577cb1b9adac8ace45d8e28662ff088a703 Mon Sep 17 00:00:00 2001 From: William Chen Date: Mon, 8 Jun 2026 00:08:41 -0400 Subject: [PATCH 02/37] Add stubs for ExtractEnumConstants and ExtractFunctionPointers --- .../SilkTouch/Mods/Common/ModLoader.cs | 2 ++ .../SilkTouch/Mods/ExtractNestedTyping.cs | 34 ++++++++++++------- ...actsCStyleEnumConstants_Field.verified.txt | 1 + ...StyleEnumConstants_ReturnType.verified.txt | 1 + 4 files changed, 25 insertions(+), 13 deletions(-) create mode 100644 tests/SilkTouch/SilkTouch/ExtractNestedTypingTests.SuccessfullyExtractsCStyleEnumConstants_Field.verified.txt create mode 100644 tests/SilkTouch/SilkTouch/ExtractNestedTypingTests.SuccessfullyExtractsCStyleEnumConstants_ReturnType.verified.txt diff --git a/sources/SilkTouch/SilkTouch/Mods/Common/ModLoader.cs b/sources/SilkTouch/SilkTouch/Mods/Common/ModLoader.cs index 5ad3bb237d..4ea293bd12 100644 --- a/sources/SilkTouch/SilkTouch/Mods/Common/ModLoader.cs +++ b/sources/SilkTouch/SilkTouch/Mods/Common/ModLoader.cs @@ -24,6 +24,8 @@ public class ModLoader nameof(ChangeNamespace) => typeof(ChangeNamespace), nameof(ChangeNativeClass) => typeof(ChangeNativeClass), nameof(ClangScraper) => typeof(ClangScraper), + nameof(ExtractEnumConstants) => typeof(ExtractEnumConstants), + nameof(ExtractFunctionPointers) => typeof(ExtractFunctionPointers), nameof(ExtractHandles) => typeof(ExtractHandles), nameof(ExtractNestedTyping) => typeof(ExtractNestedTyping), nameof(IdentifySharedPrefixes) => typeof(IdentifySharedPrefixes), diff --git a/sources/SilkTouch/SilkTouch/Mods/ExtractNestedTyping.cs b/sources/SilkTouch/SilkTouch/Mods/ExtractNestedTyping.cs index 273eee7491..67822246e7 100644 --- a/sources/SilkTouch/SilkTouch/Mods/ExtractNestedTyping.cs +++ b/sources/SilkTouch/SilkTouch/Mods/ExtractNestedTyping.cs @@ -7,20 +7,15 @@ using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.Extensions.Logging; +using Silk.NET.SilkTouch.Clang; using Silk.NET.SilkTouch.Naming; using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; namespace Silk.NET.SilkTouch.Mods; /// -/// A mod that extracts type system information nested within other types. This currently includes: -/// -/// -/// Replacing function pointers identified by their s with delegates and -/// function pointer structs. -/// -/// -/// Moving constants into their respective enums. These constants are identified by checking for an enum with +/// Moves enum constants into their respective enums. +/// These constants are identified by checking for an enum with /// a matching prefix, as identified by the enum's . /// This accounts for the below pattern seen frequently pre-C99: /// @@ -28,11 +23,24 @@ namespace Silk.NET.SilkTouch.Mods; /// #define MY_ENUM_HELLO 0 /// extern MyEnum GetMyEnum(); /// -/// -/// -/// Extracting fixed buffers and anonymous structures contained within structures into separate types. -/// -/// +/// +public class ExtractEnumConstants : Mod +{ + public override async Task ExecuteAsync(IModContext ctx, CancellationToken ct = default) { } +} + +/// +/// Replaces function pointers identified by their s +/// with delegates and function pointer structs. +/// +public class ExtractFunctionPointers : Mod +{ + public override async Task ExecuteAsync(IModContext ctx, CancellationToken ct = default) { } +} + +/// +/// Extracts nested types into their own separate types. +/// In particular, this also handles fixed buffers and anonymous structures output by . /// public partial class ExtractNestedTyping(ILogger logger) : Mod { diff --git a/tests/SilkTouch/SilkTouch/ExtractNestedTypingTests.SuccessfullyExtractsCStyleEnumConstants_Field.verified.txt b/tests/SilkTouch/SilkTouch/ExtractNestedTypingTests.SuccessfullyExtractsCStyleEnumConstants_Field.verified.txt new file mode 100644 index 0000000000..5f282702bb --- /dev/null +++ b/tests/SilkTouch/SilkTouch/ExtractNestedTypingTests.SuccessfullyExtractsCStyleEnumConstants_Field.verified.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/tests/SilkTouch/SilkTouch/ExtractNestedTypingTests.SuccessfullyExtractsCStyleEnumConstants_ReturnType.verified.txt b/tests/SilkTouch/SilkTouch/ExtractNestedTypingTests.SuccessfullyExtractsCStyleEnumConstants_ReturnType.verified.txt new file mode 100644 index 0000000000..5f282702bb --- /dev/null +++ b/tests/SilkTouch/SilkTouch/ExtractNestedTypingTests.SuccessfullyExtractsCStyleEnumConstants_ReturnType.verified.txt @@ -0,0 +1 @@ + \ No newline at end of file From a4f0a9840ceddfcd2912593a63e653c4c290e585 Mon Sep 17 00:00:00 2001 From: William Chen Date: Mon, 8 Jun 2026 02:41:20 -0400 Subject: [PATCH 03/37] Fix invalid interface implementation --- sources/SilkTouch/SilkTouch/Mods/Common/Mod.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sources/SilkTouch/SilkTouch/Mods/Common/Mod.cs b/sources/SilkTouch/SilkTouch/Mods/Common/Mod.cs index 2c72d986cb..a50a625c4f 100644 --- a/sources/SilkTouch/SilkTouch/Mods/Common/Mod.cs +++ b/sources/SilkTouch/SilkTouch/Mods/Common/Mod.cs @@ -40,7 +40,8 @@ public string PathForFullyQualified(string fullyQualified, string extension = ". } /// - public virtual void InitializeAsync(IModContext ctx, CancellationToken ct = default) { } + public virtual Task InitializeAsync(IModContext ctx, CancellationToken ct = default) => + Task.CompletedTask; /// public virtual async Task ExecuteAsync(IModContext ctx, CancellationToken ct = default) From 2a622cb70dcc8ad78201c98daf3a8ebf9f1986c8 Mon Sep 17 00:00:00 2001 From: William Chen Date: Mon, 8 Jun 2026 02:59:04 -0400 Subject: [PATCH 04/37] Work on splitting ExtractNestedTyping into 3 separate mods --- .../SilkTouch/Mods/ExtractEnumConstants.cs | 294 +++++++ .../SilkTouch/Mods/ExtractFunctionPointers.cs | 608 ++++++++++++++ .../SilkTouch/Mods/ExtractNestedTyping.cs | 742 +----------------- 3 files changed, 903 insertions(+), 741 deletions(-) create mode 100644 sources/SilkTouch/SilkTouch/Mods/ExtractEnumConstants.cs create mode 100644 sources/SilkTouch/SilkTouch/Mods/ExtractFunctionPointers.cs diff --git a/sources/SilkTouch/SilkTouch/Mods/ExtractEnumConstants.cs b/sources/SilkTouch/SilkTouch/Mods/ExtractEnumConstants.cs new file mode 100644 index 0000000000..ff1f0a25ae --- /dev/null +++ b/sources/SilkTouch/SilkTouch/Mods/ExtractEnumConstants.cs @@ -0,0 +1,294 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.Extensions.Logging; +using Silk.NET.SilkTouch.Naming; + +namespace Silk.NET.SilkTouch.Mods; + +/// +/// Moves enum constants into their respective enums. +/// These constants are identified by checking for an enum with +/// a matching prefix, as identified by the enum's . +/// This accounts for the below pattern seen frequently pre-C99: +/// +/// typedef unsigned int MyEnum; +/// #define MY_ENUM_HELLO 0 +/// extern MyEnum GetMyEnum(); +/// +/// +public partial class ExtractEnumConstants : Mod +{ + /// + public override async Task ExecuteAsync(IModContext ctx, CancellationToken ct = default) + { + await base.ExecuteAsync(ctx, ct); + + var project = ctx.SourceProject; + if (project == null) + { + return; + } + + // First pass to gather data, such as the types to extract and generate + var walker = new Walker(); + foreach (var doc in project.Documents) + { + var (fname, node) = (doc.RelativePath(), await doc.GetSyntaxRootAsync(ct)); + if (fname is null) + { + continue; + } + + walker.File = fname; + walker.Visit(node); + } + + var rewriter = new Rewriter(logger); + var (enums, constants) = walker.GetExtractedEnums(); + rewriter.ConstantsToRemove = constants; + rewriter.ExtractedEnums = enums.Keys; + + ctx.SourceProject = project; + } + + private partial class Rewriter(ILogger logger) : CSharpSyntaxRewriter + { + public IReadOnlyCollection? ConstantsToRemove { get; set; } + + public IReadOnlyCollection? ExtractedEnums { get; set; } + + public override SyntaxNode? VisitFieldDeclaration(FieldDeclarationSyntax node) + { + var ret = base.VisitFieldDeclaration(node) as FieldDeclarationSyntax; + return ret?.Declaration.Variables.Count == 0 ? null : ret; + } + + public override SyntaxNode? VisitVariableDeclarator(VariableDeclaratorSyntax node) + { + if (ConstantsToRemove?.Contains(node.Identifier.ToString()) ?? false) + { + return null; + } + return base.VisitVariableDeclarator(node); + } + } + + private class Walker : CSharpSyntaxRewriter + { + private readonly Dictionary< + string, + ( + SyntaxKind Type, + HashSet ReferencingFileDirs, + HashSet ReferencingNamespaces + )? + > _numericTypeNames = new(); + + /// + /// Tracks the name and value of constants discovered. + /// + private readonly Dictionary _constants = []; + + public string? File { get; set; } + + public override SyntaxNode? VisitPredefinedType(PredefinedTypeSyntax node) + { + var nativeTypeName = GetNativeTypeNameForPredefinedType(node).ToString(); + if (nativeTypeName.Length > 0) + { + // Detect type discrepancies. + var thisType = node.Keyword.Kind(); + if (!_numericTypeNames.TryGetValue(nativeTypeName, out var numericTypeName)) + { + _numericTypeNames[nativeTypeName] = numericTypeName = (thisType, [], []); + } + + if ( + thisType + is not ( + SyntaxKind.ByteKeyword + or SyntaxKind.SByteKeyword + or SyntaxKind.ShortKeyword + or SyntaxKind.UShortKeyword + or SyntaxKind.IntKeyword + or SyntaxKind.UIntKeyword + or SyntaxKind.LongKeyword + or SyntaxKind.ULongKeyword + ) + || thisType != numericTypeName?.Type + ) + { + _numericTypeNames[nativeTypeName] = numericTypeName = null; + } + + if (numericTypeName is { } theTypeDetails) + { + theTypeDetails.ReferencingNamespaces.Add(node.NamespaceFromSyntaxNode()); + if (File?[..File.LastIndexOf('/')] is { } dir) + { + theTypeDetails.ReferencingFileDirs.Add(dir); + } + } + } + return base.VisitPredefinedType(node); + } + + // This code can probably be better. + public ( + Dictionary< + string, + (EnumDeclarationSyntax, HashSet, HashSet) + > ExtractedEnums, + HashSet ExtractedConstants + ) GetExtractedEnums() + { + var ineligibleConstants = new HashSet(); + var extractedConstants = new HashSet(); + var extractedEnums = new Dictionary< + string, + (EnumDeclarationSyntax, HashSet, HashSet) + >(_numericTypeNames.Count); + + // Try and find constants for each of the enums we've found. + // We do this in descending length order to ensure that we find the longest match for constant names to enum + // names. + foreach ( + var (enumName, enumType) in _numericTypeNames.OrderByDescending(x => x.Key.Length) + ) + { + var enumTrimmingName = NameSplitter.Underscore(enumName); + (EnumDeclarationSyntax, HashSet, HashSet)? extractedEnum = enumType + is { } theType + ? ( + SyntaxFactory + .EnumDeclaration(enumName) + .AddBaseListTypes( + SyntaxFactory.SimpleBaseType( + SyntaxFactory.PredefinedType(SyntaxFactory.Token(theType.Type)) + ) + ) + .AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword)), + theType.ReferencingFileDirs, + theType.ReferencingNamespaces + ) + : null; + + // Look through all of the constants and see whether they start with our enum name. + foreach (var (constant, value) in _constants) + { + // We want to account for PascalCase vs SCREAMING_SNAKE_CASE differences (for example) so we do + // four passes (for each combination of the original name vs trimming name, the latter of which + // taking casing into account). It is possible that this could be expanded, but this should be done + // carefully to ensure we don't light up prematurely. + var nextConst = false; + var trimmingName = NameSplitter.Underscore(constant); + foreach ( + var enumCandidate in (ReadOnlySpan)[enumName, enumTrimmingName] + ) + { + foreach ( + var constCandidate in (ReadOnlySpan)[constant, trimmingName] + ) + { + // Make sure the constant name starts with the enum name, and that there is clearly a word + // gap after the enum name in the constant name e.g. API_BlendOp doesn't pull in + // API_BLEND_OPAQUE but it does pull in API_BLEND_OP_ADD (or API_BLENDOP_ADD). + // I wouldn't feel safe relaxing this right now, despite there being obvious use cases. + // Perhaps as a future improvement we can try to walk back the enum's trimming name, check + // that there are no other enums that conflict with that shorter trimming name, and then try + // to widen the scope. So for example, if we have found nothing for API_BlendOp then we'd + // just try API_Blend (provided there's no API_BlendFactor, API_Blend, or any other + // conflicts) which would then sweep up API_BLEND_OPAQUE. We'd then keep the original enum + // names to stay in-keeping with the native API. TODO investigate this + if ( + !constCandidate.StartsWith( + enumCandidate, + StringComparison.OrdinalIgnoreCase + ) + || ( + constCandidate[enumCandidate.Length] != '_' + && char.IsUpper(constCandidate[enumCandidate.Length - 1]) + == char.IsUpper(constCandidate[enumCandidate.Length]) + ) + ) + { + continue; + } + + // We don't generate enums that have had inconsistent usage (e.g. int vs short vs long) but + // if we are able to map constants into those enums, we still want to ensure we don't go and + // map it to a less relevant enum as a result. So we add the constant to a separate HashSet, + // with which we remove the constant from the _constants dictionary but don't return it to + // the Rewriter, ensuring it is not removed (at is has not been mapped to an eligible enum). + (enumType is null ? ineligibleConstants : extractedConstants).Add( + constant + ); + nextConst = true; + if (extractedEnum is not { } theExtractedEnum) + { + break; + } + + theExtractedEnum.Item1 = theExtractedEnum.Item1.AddMembers( + SyntaxFactory + .EnumMemberDeclaration(constant) + .WithEqualsValue(SyntaxFactory.EqualsValueClause(value)) + ); + extractedEnum = theExtractedEnum; + break; + } + + if (nextConst) + { + break; + } + } + } + + // Remove the constants that we've mapped into enums + foreach ( + var constant in (IEnumerable) + [.. ineligibleConstants, .. extractedConstants] + ) + { + _constants.Remove(constant); + } + + ineligibleConstants.Clear(); + if (extractedEnum is { Item1.Members.Count: > 0 }) + { + extractedEnums[enumName] = extractedEnum.Value; + } + } + + return (extractedEnums, extractedConstants); + } + + public override SyntaxNode? VisitFieldDeclaration(FieldDeclarationSyntax node) + { + if (node.Modifiers.Any(SyntaxKind.ConstKeyword)) + { + foreach (var vardec in node.Declaration.Variables) + { + if (vardec.Initializer is null) + { + continue; + } + + _constants.Add(vardec.Identifier.ToString(), vardec.Initializer.Value); + } + } + return base.VisitFieldDeclaration(node); + } + + public override SyntaxNode? VisitEnumDeclaration(EnumDeclarationSyntax node) + { + InvalidateIfSeen(_numericTypeNames, node.Identifier.ToString()); + return base.VisitEnumDeclaration(node); + } + } +} diff --git a/sources/SilkTouch/SilkTouch/Mods/ExtractFunctionPointers.cs b/sources/SilkTouch/SilkTouch/Mods/ExtractFunctionPointers.cs new file mode 100644 index 0000000000..517bda842d --- /dev/null +++ b/sources/SilkTouch/SilkTouch/Mods/ExtractFunctionPointers.cs @@ -0,0 +1,608 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Text.RegularExpressions; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.Extensions.Logging; +using Silk.NET.SilkTouch.Naming; + +namespace Silk.NET.SilkTouch.Mods; + +/// +/// Replaces function pointers identified by their s +/// with delegates and function pointer structs. +/// +public partial class ExtractFunctionPointers : Mod +{ + /// + public override async Task ExecuteAsync(IModContext ctx, CancellationToken ct = default) + { + await base.ExecuteAsync(ctx, ct); + + var project = ctx.SourceProject; + if (project == null) + { + return; + } + + // First pass to gather data, such as the types to extract and generate + var walker = new Walker(); + foreach (var doc in project.Documents) + { + var (fname, node) = (doc.RelativePath(), await doc.GetSyntaxRootAsync(ct)); + if (fname is null) + { + continue; + } + + walker.File = fname; + walker.Visit(node); + } + + // Add documents for each extracted function pointer + // This is moved out of the foreach statement for better debuggability + var extractedFunctionPointers = rewriter + .FunctionPointerTypes.Values + // .Where(x => x.IsUnique) + .SelectMany(x => + (IEnumerable<(MemberDeclarationSyntax, string, HashSet, HashSet)>) + [ + ( + x.Delegate, + x.Delegate.Identifier.ToString(), + x.ReferencingFileDirs, + x.ReferencingNamespaces + ), + ( + x.Pfn, + x.Pfn.Identifier.ToString(), + x.ReferencingFileDirs, + x.ReferencingNamespaces + ), + ] + ) + .Concat( + enums.Select(x => + ( + (MemberDeclarationSyntax)x.Value.Item1, + x.Value.Item1.Identifier.ToString(), + x.Value.Item2, + x.Value.Item3 + ) + ) + ) + .ToList(); + + foreach (var (typeDecl, identifier, fileDirs, namespaces) in extractedFunctionPointers) + { + var ns = NameUtils.FindCommonPrefix(namespaces, true, false, true); + var dir = NameUtils.FindCommonPrefix(fileDirs, true, false, true).TrimEnd('/'); + project = project + ?.AddDocument( + $"{identifier}.gen.cs", + SyntaxFactory + .CompilationUnit() + .WithMembers( + ns is { Length: > 0 } + ? SyntaxFactory.SingletonList( + SyntaxFactory + .FileScopedNamespaceDeclaration( + ModUtils.NamespaceIntoIdentifierName(ns.TrimEnd('.')) + ) + .WithMembers(SingletonList(typeDecl)) + ) + : SingletonList(typeDecl) + ), + filePath: project.FullPath($"{dir}/{identifier}.gen.cs") + ) + .Project; + } + + ctx.SourceProject = project; + } + + private partial class Rewriter(ILogger logger) : CSharpSyntaxRewriter + { + private string? _typeNameFromOuterFunctionPointer; + private string? _fallbackFromOuterFunctionPointer; + + public Dictionary< + string, + ( + StructDeclarationSyntax Pfn, + DelegateDeclarationSyntax Delegate, + HashSet ReferencingFileDirs, + HashSet ReferencingNamespaces + ) + > FunctionPointerTypes { get; set; } = []; + + public override SyntaxNode? VisitFunctionPointerType(FunctionPointerTypeSyntax node) + { + // Walk up the type. We expect only pointers above us, but we could encounter a function pointer type in + // which case we just ignore all this as we should already have a _currentNativeTypeName. Anything else and + // we don't have enough context for a fallback. + var current = node.Parent; + var indirectionLevels = 0; + while (current is PointerTypeSyntax) + { + indirectionLevels++; + current = current.Parent; + } + + // As above, get the native type name if we can and also get a fallback name based on context. + var (currentNativeTypeName, fallback) = current switch + { + MethodDeclarationSyntax meth => ( + meth.AttributeLists.GetNativeTypeName(SyntaxKind.ReturnKeyword), + $"{meth.Identifier}_r" + ), + ParameterSyntax { Parent.Parent: MethodDeclarationSyntax meth } param => ( + param.AttributeLists.GetNativeTypeName(), + $"{meth.Identifier}_{param.Identifier}" + ), + VariableDeclarationSyntax + { + Parent: FieldDeclarationSyntax { Parent: BaseTypeDeclarationSyntax type } fld + } vardec => ( + fld.AttributeLists.GetNativeTypeName(), + $"{type.Identifier}_{vardec.Variables[0].Identifier}" + ), + _ => (null, null), + }; + + // If the native type name is actually the function pointer signature (i.e. not through a typedef) then we + // should pass the native type name down when recursing. + fallback = _fallbackFromOuterFunctionPointer ?? fallback; + currentNativeTypeName = + (_typeNameFromOuterFunctionPointer ?? currentNativeTypeName)?.Trim() ?? fallback; + string[]? recursiveTypeNames = null; + if (currentNativeTypeName.AsSpan().ContainsAnyExcept(NameUtils.IdentifierChars)) + { + var match = FunctionPointerNativeTypeNameRegex().Match(currentNativeTypeName!); + if (match.Success) + { + currentNativeTypeName = fallback; + + // NOTE: We expect the groups to be as follows: + // 0 = everything + // 1 = return type + // 2 = indirection levels + 1 + // 3 = comma separated parameter types + recursiveTypeNames = new string[ + 1 + + (match.Groups[3].Value.Length > 0 ? 1 : 0) + + match.Groups[3].Value.AsSpan().Count(',') + ]; + if (match.Groups[2].Value.AsSpan().Count('*') != indirectionLevels + 1) + { + logger.LogWarning( + "Unable to deal with function pointer usage at {} - mismatch of indirection " + + "levels: {} for {}", + node.GetLocation().GetLineSpan(), + node, + currentNativeTypeName + ); + return node; + } + + recursiveTypeNames[^1] = match.Groups[1].Value; + var @params = match + .Groups[3] + .Value.Split( + ',', + StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries + ); + for (var i = 0; i < @params.Length; i++) + { + recursiveTypeNames[i] = @params[i]; + } + } + else + { + // Maybe it's a pointer type? + var idSpan = currentNativeTypeName.AsSpan(); + if (idSpan.StartsWith("const ")) + { + idSpan = idSpan["const ".Length..]; + } + + // If the indirection levels match (and the only other non-identifier characters are whitespace) + // then we can use the identifier as the native name. + idSpan = idSpan.Trim(); + var badStart = idSpan.IndexOfAnyExcept(NameUtils.IdentifierChars); + var bad = idSpan[badStart..]; + currentNativeTypeName = + badStart == -1 + || ( + bad.Count('*') == indirectionLevels + && bad.Count(' ') == bad.Length - indirectionLevels + ) + ? idSpan[..badStart].ToString() + : fallback; + } + } + + if (currentNativeTypeName is null) + { + logger.LogWarning( + "Unable to deal with function pointer usage at {} - terminated at {}: {}", + node.GetLocation().GetLineSpan(), + current?.GetType().Name ?? "null", + current + ); + return node; + } + + // Assert that our state is valid given the tests we've done above before recursing. + Debug.Assert( + _fallbackFromOuterFunctionPointer is not null + == node.Ancestors().OfType().Any() + ); + + // Ensure that we've recursively generated and fixed up any function pointers contained within this function + // pointer. + var ns = node.NamespaceFromSyntaxNode(); + node = node.WithParameterList( + node.ParameterList.WithParameters( + SyntaxFactory.SeparatedList( + node.ParameterList.Parameters.Select( + (x, i) => + { + var typeNameBefore = _typeNameFromOuterFunctionPointer; + var fallbackBefore = _fallbackFromOuterFunctionPointer; + _typeNameFromOuterFunctionPointer = recursiveTypeNames?[i]; + _fallbackFromOuterFunctionPointer = + $"{currentNativeTypeName}_p{i}"; + var ret = base.Visit(x); + _typeNameFromOuterFunctionPointer = typeNameBefore; + _fallbackFromOuterFunctionPointer = fallbackBefore; + return ret; + } + ) + .OfType() + ) + ) + ); + + // Generate the types if we haven't already. + if (!FunctionPointerTypes.TryGetValue(currentNativeTypeName, out var pfnInfo)) + { + var (pfn, @delegate) = CreateFunctionPointerTypes( + currentNativeTypeName, + $"{currentNativeTypeName}Delegate", + ( + currentNativeTypeName == fallback + ? SyntaxFactory.SingletonList( + SyntaxFactory.AttributeList( + SyntaxFactory.SingletonSeparatedList( + SyntaxFactory.Attribute( + SyntaxFactory.IdentifierName("Transformed") + ) + ) + ) + ) + : default + ).WithNativeName(currentNativeTypeName), + ( + currentNativeTypeName == fallback + ? SyntaxFactory.SingletonList( + SyntaxFactory.AttributeList( + SyntaxFactory.SingletonSeparatedList( + SyntaxFactory.Attribute( + SyntaxFactory.IdentifierName("Transformed") + ) + ) + ) + ) + : default + ) + .WithNativeName(currentNativeTypeName) + .AddReferencedNameAffix( + NameAffixType.Prefix, + "FunctionPointerParent", + currentNativeTypeName + ) + .AddNameAffix( + NameAffixType.Suffix, + "FunctionPointerDelegateType", + "Delegate" + ), + node + ); + FunctionPointerTypes[currentNativeTypeName] = pfnInfo = (pfn, @delegate, [], []); + } + + // Ensure this visitation is used to determine the namespace/location. + pfnInfo.ReferencingNamespaces.Add(ns); + if (File?[..File.LastIndexOf('/')] is { } dir) + { + pfnInfo.ReferencingFileDirs.Add(dir); + } + + return SyntaxFactory.IdentifierName(currentNativeTypeName); + } + + [GeneratedRegex( + @"^((?:[A-Za-z0-9\s\*_]|\[[0-9]*\])+)\((\*)+\)\(((?:(?:[A-Za-z0-9\s\*_]|\[[0-9]*\])+,?)*)\)" + )] + private partial Regex FunctionPointerNativeTypeNameRegex(); + } + + private class Walker : CSharpSyntaxRewriter + { + private static ( + StructDeclarationSyntax Pfn, + DelegateDeclarationSyntax Delegate + ) CreateFunctionPointerTypes( + string pfnName, + string delegateName, + SyntaxList pfnAttrLists, + SyntaxList delegateAttrLists, + FunctionPointerTypeSyntax rawPfn + ) + { + // Ported from https://github.com/dotnet/Silk.NET/blob/d30cc43b/src/Core/Silk.NET.BuildTools/Bind/StructWriter.cs#L744-L774 + var pfn = SyntaxFactory + .StructDeclaration(pfnName) + .WithModifiers( + SyntaxFactory.TokenList( + SyntaxFactory.Token(SyntaxKind.PublicKeyword), + SyntaxFactory.Token(SyntaxKind.UnsafeKeyword), + SyntaxFactory.Token(SyntaxKind.ReadOnlyKeyword) + ) + ) + .WithBaseList( + SyntaxFactory.BaseList( + SyntaxFactory.SingletonSeparatedList( + SyntaxFactory.SimpleBaseType( + SyntaxFactory.IdentifierName("IDisposable") + ) + ) + ) + ) + .WithAttributeLists(pfnAttrLists) + .WithMembers( + SyntaxFactory.List( + [ + SyntaxFactory + .FieldDeclaration( + SyntaxFactory.VariableDeclaration( + SyntaxFactory.PointerType( + SyntaxFactory.PredefinedType( + SyntaxFactory.Token(SyntaxKind.VoidKeyword) + ) + ), + SyntaxFactory.SingletonSeparatedList( + SyntaxFactory.VariableDeclarator("_pointer") + ) + ) + ) + .WithModifiers( + SyntaxFactory.TokenList( + SyntaxFactory.Token(SyntaxKind.PrivateKeyword), + SyntaxFactory.Token(SyntaxKind.ReadOnlyKeyword) + ) + ), + SyntaxFactory + .PropertyDeclaration(rawPfn, "Handle") + .WithModifiers( + SyntaxFactory.TokenList( + SyntaxFactory.Token(SyntaxKind.PublicKeyword) + ) + ) + .WithExpressionBody( + SyntaxFactory.ArrowExpressionClause( + SyntaxFactory.CastExpression( + rawPfn, + SyntaxFactory.IdentifierName("_pointer") + ) + ) + ) + .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)), + SyntaxFactory + .ConstructorDeclaration(pfnName) + .WithParameterList( + SyntaxFactory.ParameterList( + SyntaxFactory.SingletonSeparatedList( + SyntaxFactory + .Parameter(SyntaxFactory.Identifier("ptr")) + .WithType(rawPfn) + ) + ) + ) + .WithExpressionBody( + SyntaxFactory.ArrowExpressionClause( + SyntaxFactory.AssignmentExpression( + SyntaxKind.SimpleAssignmentExpression, + SyntaxFactory.IdentifierName("_pointer"), + SyntaxFactory.IdentifierName("ptr") + ) + ) + ) + .WithModifiers( + SyntaxFactory.TokenList( + SyntaxFactory.Token(SyntaxKind.PublicKeyword) + ) + ) + .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)), + SyntaxFactory + .ConstructorDeclaration(pfnName) + .WithParameterList( + SyntaxFactory.ParameterList( + SyntaxFactory.SingletonSeparatedList( + SyntaxFactory + .Parameter(SyntaxFactory.Identifier("proc")) + .WithType( + SyntaxFactory.IdentifierName(delegateName) + ) + ) + ) + ) + .WithExpressionBody( + SyntaxFactory.ArrowExpressionClause( + SyntaxFactory.AssignmentExpression( + SyntaxKind.SimpleAssignmentExpression, + SyntaxFactory.IdentifierName("_pointer"), + SyntaxFactory.InvocationExpression( + SyntaxFactory.MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + SyntaxFactory.IdentifierName("SilkMarshal"), + SyntaxFactory.IdentifierName("DelegateToPtr") + ), + SyntaxFactory.ArgumentList( + SyntaxFactory.SingletonSeparatedList( + SyntaxFactory.Argument( + SyntaxFactory.IdentifierName("proc") + ) + ) + ) + ) + ) + ) + ) + .WithModifiers( + SyntaxFactory.TokenList( + SyntaxFactory.Token(SyntaxKind.PublicKeyword) + ) + ) + .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)), + SyntaxFactory + .MethodDeclaration( + SyntaxFactory.PredefinedType( + SyntaxFactory.Token(SyntaxKind.VoidKeyword) + ), + "Dispose" + ) + .WithExpressionBody( + SyntaxFactory.ArrowExpressionClause( + SyntaxFactory.InvocationExpression( + SyntaxFactory.MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + SyntaxFactory.IdentifierName("SilkMarshal"), + SyntaxFactory.IdentifierName("Free") + ), + SyntaxFactory.ArgumentList( + SyntaxFactory.SingletonSeparatedList( + SyntaxFactory.Argument( + SyntaxFactory.IdentifierName("_pointer") + ) + ) + ) + ) + ) + ) + .WithModifiers( + SyntaxFactory.TokenList( + SyntaxFactory.Token(SyntaxKind.PublicKeyword) + ) + ) + .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)), + SyntaxFactory + .ConversionOperatorDeclaration( + SyntaxFactory.Token(SyntaxKind.ImplicitKeyword), + SyntaxFactory.IdentifierName(pfnName) + ) + .WithParameterList( + SyntaxFactory.ParameterList( + SyntaxFactory.SingletonSeparatedList( + SyntaxFactory + .Parameter(SyntaxFactory.Identifier("pfn")) + .WithType(rawPfn) + ) + ) + ) + .WithModifiers( + SyntaxFactory.TokenList( + SyntaxFactory.Token(SyntaxKind.PublicKeyword), + SyntaxFactory.Token(SyntaxKind.StaticKeyword) + ) + ) + .WithExpressionBody( + SyntaxFactory.ArrowExpressionClause( + SyntaxFactory.ImplicitObjectCreationExpression( + SyntaxFactory.ArgumentList( + SyntaxFactory.SingletonSeparatedList( + SyntaxFactory.Argument( + SyntaxFactory.IdentifierName("pfn") + ) + ) + ), + null + ) + ) + ) + .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)), + SyntaxFactory + .ConversionOperatorDeclaration( + SyntaxFactory.Token(SyntaxKind.ImplicitKeyword), + rawPfn + ) + .WithParameterList( + SyntaxFactory.ParameterList( + SyntaxFactory.SingletonSeparatedList( + SyntaxFactory + .Parameter(SyntaxFactory.Identifier("pfn")) + .WithType(SyntaxFactory.IdentifierName(pfnName)) + ) + ) + ) + .WithModifiers( + SyntaxFactory.TokenList( + SyntaxFactory.Token(SyntaxKind.PublicKeyword), + SyntaxFactory.Token(SyntaxKind.StaticKeyword) + ) + ) + .WithExpressionBody( + SyntaxFactory.ArrowExpressionClause( + SyntaxFactory.CastExpression( + rawPfn, + SyntaxFactory.MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + SyntaxFactory.IdentifierName("pfn"), + SyntaxFactory.IdentifierName("_pointer") + ) + ) + ) + ) + .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)), + // TODO invoke method? + ] + ) + ); + + var @delegate = SyntaxFactory + .DelegateDeclaration( + rawPfn.ParameterList.Parameters.Last().Type, + SyntaxFactory.Identifier(delegateName) + ) + .WithModifiers( + SyntaxFactory.TokenList( + SyntaxFactory.Token(SyntaxKind.PublicKeyword), + SyntaxFactory.Token(SyntaxKind.UnsafeKeyword) + ) + ) + .WithAttributeLists(delegateAttrLists) + .WithParameterList( + SyntaxFactory.ParameterList( + SyntaxFactory.SeparatedList( + rawPfn + .ParameterList.Parameters.SkipLast(1) + .Select( + (y, i) => + SyntaxFactory.Parameter( + y.AttributeLists, + y.Modifiers, + y.Type, + SyntaxFactory.Identifier($"arg{i}"), + null + ) + ) + ) + ) + ); + return (pfn, @delegate); + } + } +} diff --git a/sources/SilkTouch/SilkTouch/Mods/ExtractNestedTyping.cs b/sources/SilkTouch/SilkTouch/Mods/ExtractNestedTyping.cs index 67822246e7..ae8d690c78 100644 --- a/sources/SilkTouch/SilkTouch/Mods/ExtractNestedTyping.cs +++ b/sources/SilkTouch/SilkTouch/Mods/ExtractNestedTyping.cs @@ -1,7 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Diagnostics; using System.Text.RegularExpressions; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; @@ -13,31 +12,6 @@ namespace Silk.NET.SilkTouch.Mods; -/// -/// Moves enum constants into their respective enums. -/// These constants are identified by checking for an enum with -/// a matching prefix, as identified by the enum's . -/// This accounts for the below pattern seen frequently pre-C99: -/// -/// typedef unsigned int MyEnum; -/// #define MY_ENUM_HELLO 0 -/// extern MyEnum GetMyEnum(); -/// -/// -public class ExtractEnumConstants : Mod -{ - public override async Task ExecuteAsync(IModContext ctx, CancellationToken ct = default) { } -} - -/// -/// Replaces function pointers identified by their s -/// with delegates and function pointer structs. -/// -public class ExtractFunctionPointers : Mod -{ - public override async Task ExecuteAsync(IModContext ctx, CancellationToken ct = default) { } -} - /// /// Extracts nested types into their own separate types. /// In particular, this also handles fixed buffers and anonymous structures output by . @@ -71,10 +45,6 @@ public override async Task ExecuteAsync(IModContext ctx, CancellationToken ct = // Second pass to modify existing files as per our discovery. var rewriter = new Rewriter(logger); - // rewriter.FunctionPointerTypes = walker.GetFunctionPointerTypes(); - var (enums, constants) = walker.GetExtractedEnums(); - rewriter.ConstantsToRemove = constants; - rewriter.ExtractedEnums = enums.Keys; foreach (var docId in project.DocumentIds) { var doc = @@ -131,62 +101,6 @@ rewriter.Namespace is not null rewriter.ExtractedNestedStructs.Clear(); } - // Add documents for each extracted function pointer - // This is moved out of the foreach statement for better debuggability - var extractedFunctionPointers = rewriter - .FunctionPointerTypes.Values //.Where(x => x.IsUnique) - .SelectMany(x => - (IEnumerable<(MemberDeclarationSyntax, string, HashSet, HashSet)>) - [ - ( - x.Delegate, - x.Delegate.Identifier.ToString(), - x.ReferencingFileDirs, - x.ReferencingNamespaces - ), - ( - x.Pfn, - x.Pfn.Identifier.ToString(), - x.ReferencingFileDirs, - x.ReferencingNamespaces - ), - ] - ) - .Concat( - enums.Select(x => - ( - (MemberDeclarationSyntax)x.Value.Item1, - x.Value.Item1.Identifier.ToString(), - x.Value.Item2, - x.Value.Item3 - ) - ) - ) - .ToList(); - - foreach (var (typeDecl, identifier, fileDirs, namespaces) in extractedFunctionPointers) - { - var ns = NameUtils.FindCommonPrefix(namespaces, true, false, true); - var dir = NameUtils.FindCommonPrefix(fileDirs, true, false, true).TrimEnd('/'); - project = project - ?.AddDocument( - $"{identifier}.gen.cs", - CompilationUnit() - .WithMembers( - ns is { Length: > 0 } - ? SingletonList( - FileScopedNamespaceDeclaration( - ModUtils.NamespaceIntoIdentifierName(ns.TrimEnd('.')) - ) - .WithMembers(SingletonList(typeDecl)) - ) - : SingletonList(typeDecl) - ), - filePath: project.FullPath($"{dir}/{identifier}.gen.cs") - ) - .Project; - } - ctx.SourceProject = project; } @@ -244,26 +158,12 @@ string nativeTypeName } } - partial class Rewriter(ILogger logger) : CSharpSyntaxRewriter + private partial class Rewriter(ILogger logger) : CSharpSyntaxRewriter { private Dictionary _typeRenames = []; public List ExtractedNestedStructs { get; } = []; - public Dictionary< - string, - ( - StructDeclarationSyntax Pfn, - DelegateDeclarationSyntax Delegate, - HashSet ReferencingFileDirs, - HashSet ReferencingNamespaces - ) - > FunctionPointerTypes { get; set; } = []; - - public IReadOnlyCollection? ConstantsToRemove { get; set; } - - public IReadOnlyCollection? ExtractedEnums { get; set; } - public string? Namespace { get; set; } public string? File { get; set; } @@ -293,21 +193,6 @@ is not null return base.VisitPredefinedType(node); } - public override SyntaxNode? VisitFieldDeclaration(FieldDeclarationSyntax node) - { - var ret = base.VisitFieldDeclaration(node) as FieldDeclarationSyntax; - return ret?.Declaration.Variables.Count == 0 ? null : ret; - } - - public override SyntaxNode? VisitVariableDeclarator(VariableDeclaratorSyntax node) - { - if (ConstantsToRemove?.Contains(node.Identifier.ToString()) ?? false) - { - return null; - } - return base.VisitVariableDeclarator(node); - } - public override SyntaxNode? VisitStructDeclaration(StructDeclarationSyntax node) { // Extract nested structs @@ -368,632 +253,7 @@ is not { } key return ret; } - private string? _typeNameFromOuterFunctionPointer; - private string? _fallbackFromOuterFunctionPointer; - - public override SyntaxNode? VisitFunctionPointerType(FunctionPointerTypeSyntax node) - { - // Walk up the type. We expect only pointers above us, but we could encounter a function pointer type in - // which case we just ignore all this as we should already have a _currentNativeTypeName. Anything else and - // we don't have enough context for a fallback. - var current = node.Parent; - var indirectionLevels = 0; - while (current is PointerTypeSyntax) - { - indirectionLevels++; - current = current.Parent; - } - - // As above, get the native type name if we can and also get a fallback name based on context. - var (currentNativeTypeName, fallback) = current switch - { - MethodDeclarationSyntax meth => ( - meth.AttributeLists.GetNativeTypeName(SyntaxKind.ReturnKeyword), - $"{meth.Identifier}_r" - ), - ParameterSyntax { Parent.Parent: MethodDeclarationSyntax meth } param => ( - param.AttributeLists.GetNativeTypeName(), - $"{meth.Identifier}_{param.Identifier}" - ), - VariableDeclarationSyntax - { - Parent: FieldDeclarationSyntax { Parent: BaseTypeDeclarationSyntax type } fld - } vardec => ( - fld.AttributeLists.GetNativeTypeName(), - $"{type.Identifier}_{vardec.Variables[0].Identifier}" - ), - _ => (null, null), - }; - - // If the native type name is actually the function pointer signature (i.e. not through a typedef) then we - // should pass the native type name down when recursing. - fallback = _fallbackFromOuterFunctionPointer ?? fallback; - currentNativeTypeName = - (_typeNameFromOuterFunctionPointer ?? currentNativeTypeName)?.Trim() ?? fallback; - string[]? recursiveTypeNames = null; - if (currentNativeTypeName.AsSpan().ContainsAnyExcept(NameUtils.IdentifierChars)) - { - var match = FunctionPointerNativeTypeNameRegex().Match(currentNativeTypeName!); - if (match.Success) - { - currentNativeTypeName = fallback; - - // NOTE: We expect the groups to be as follows: - // 0 = everything - // 1 = return type - // 2 = indirection levels + 1 - // 3 = comma separated parameter types - recursiveTypeNames = new string[ - 1 - + (match.Groups[3].Value.Length > 0 ? 1 : 0) - + match.Groups[3].Value.AsSpan().Count(',') - ]; - if (match.Groups[2].Value.AsSpan().Count('*') != indirectionLevels + 1) - { - logger.LogWarning( - "Unable to deal with function pointer usage at {} - mismatch of indirection " - + "levels: {} for {}", - node.GetLocation().GetLineSpan(), - node, - currentNativeTypeName - ); - return node; - } - - recursiveTypeNames[^1] = match.Groups[1].Value; - var @params = match - .Groups[3] - .Value.Split( - ',', - StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries - ); - for (var i = 0; i < @params.Length; i++) - { - recursiveTypeNames[i] = @params[i]; - } - } - else - { - // Maybe it's a pointer type? - var idSpan = currentNativeTypeName.AsSpan(); - if (idSpan.StartsWith("const ")) - { - idSpan = idSpan["const ".Length..]; - } - - // If the indirection levels match (and the only other non-identifier characters are whitespace) - // then we can use the identifier as the native name. - idSpan = idSpan.Trim(); - var badStart = idSpan.IndexOfAnyExcept(NameUtils.IdentifierChars); - var bad = idSpan[badStart..]; - currentNativeTypeName = - badStart == -1 - || ( - bad.Count('*') == indirectionLevels - && bad.Count(' ') == bad.Length - indirectionLevels - ) - ? idSpan[..badStart].ToString() - : fallback; - } - } - - if (currentNativeTypeName is null) - { - logger.LogWarning( - "Unable to deal with function pointer usage at {} - terminated at {}: {}", - node.GetLocation().GetLineSpan(), - current?.GetType().Name ?? "null", - current - ); - return node; - } - - // Assert that our state is valid given the tests we've done above before recursing. - Debug.Assert( - _fallbackFromOuterFunctionPointer is not null - == node.Ancestors().OfType().Any() - ); - - // Ensure that we've recursively generated and fixed up any function pointers contained within this function - // pointer. - var ns = node.NamespaceFromSyntaxNode(); - node = node.WithParameterList( - node.ParameterList.WithParameters( - SeparatedList( - node.ParameterList.Parameters.Select( - (x, i) => - { - var typeNameBefore = _typeNameFromOuterFunctionPointer; - var fallbackBefore = _fallbackFromOuterFunctionPointer; - _typeNameFromOuterFunctionPointer = recursiveTypeNames?[i]; - _fallbackFromOuterFunctionPointer = - $"{currentNativeTypeName}_p{i}"; - var ret = base.Visit(x); - _typeNameFromOuterFunctionPointer = typeNameBefore; - _fallbackFromOuterFunctionPointer = fallbackBefore; - return ret; - } - ) - .OfType() - ) - ) - ); - - // Generate the types if we haven't already. - if (!FunctionPointerTypes.TryGetValue(currentNativeTypeName, out var pfnInfo)) - { - var (pfn, @delegate) = CreateFunctionPointerTypes( - currentNativeTypeName, - $"{currentNativeTypeName}Delegate", - ( - currentNativeTypeName == fallback - ? SingletonList( - AttributeList( - SingletonSeparatedList(Attribute(IdentifierName("Transformed"))) - ) - ) - : default - ).WithNativeName(currentNativeTypeName), - ( - currentNativeTypeName == fallback - ? SingletonList( - AttributeList( - SingletonSeparatedList(Attribute(IdentifierName("Transformed"))) - ) - ) - : default - ) - .WithNativeName(currentNativeTypeName) - .AddReferencedNameAffix( - NameAffixType.Prefix, - "FunctionPointerParent", - currentNativeTypeName - ) - .AddNameAffix( - NameAffixType.Suffix, - "FunctionPointerDelegateType", - "Delegate" - ), - node - ); - FunctionPointerTypes[currentNativeTypeName] = pfnInfo = (pfn, @delegate, [], []); - } - - // Ensure this visitation is used to determine the namespace/location. - pfnInfo.ReferencingNamespaces.Add(ns); - if (File?[..File.LastIndexOf('/')] is { } dir) - { - pfnInfo.ReferencingFileDirs.Add(dir); - } - - return IdentifierName(currentNativeTypeName); - } - [GeneratedRegex("^_([a-zA-Z0-9_]*)_e__(Union|Struct|FixedBuffer)$")] private partial Regex GeneratedNestedTypeRegex(); - - [GeneratedRegex( - @"^((?:[A-Za-z0-9\s\*_]|\[[0-9]*\])+)\((\*)+\)\(((?:(?:[A-Za-z0-9\s\*_]|\[[0-9]*\])+,?)*)\)" - )] - private partial Regex FunctionPointerNativeTypeNameRegex(); - } - - class Walker : CSharpSyntaxRewriter - { - private readonly Dictionary< - string, - ( - SyntaxKind Type, - HashSet ReferencingFileDirs, - HashSet ReferencingNamespaces - )? - > _numericTypeNames = new(); - - /// - /// Tracks the name and value of constants discovered. - /// - private readonly Dictionary _constants = []; - - public string? File { get; set; } - - public override SyntaxNode? VisitPredefinedType(PredefinedTypeSyntax node) - { - var nativeTypeName = GetNativeTypeNameForPredefinedType(node).ToString(); - if (nativeTypeName.Length > 0) - { - // Detect type discrepancies. - var thisType = node.Keyword.Kind(); - if (!_numericTypeNames.TryGetValue(nativeTypeName, out var numericTypeName)) - { - _numericTypeNames[nativeTypeName] = numericTypeName = (thisType, [], []); - } - - if ( - thisType - is not ( - SyntaxKind.ByteKeyword - or SyntaxKind.SByteKeyword - or SyntaxKind.ShortKeyword - or SyntaxKind.UShortKeyword - or SyntaxKind.IntKeyword - or SyntaxKind.UIntKeyword - or SyntaxKind.LongKeyword - or SyntaxKind.ULongKeyword - ) - || thisType != numericTypeName?.Type - ) - { - _numericTypeNames[nativeTypeName] = numericTypeName = null; - } - - if (numericTypeName is { } theTypeDetails) - { - theTypeDetails.ReferencingNamespaces.Add(node.NamespaceFromSyntaxNode()); - if (File?[..File.LastIndexOf('/')] is { } dir) - { - theTypeDetails.ReferencingFileDirs.Add(dir); - } - } - } - return base.VisitPredefinedType(node); - } - - // This code can probably be better. - public ( - Dictionary< - string, - (EnumDeclarationSyntax, HashSet, HashSet) - > ExtractedEnums, - HashSet ExtractedConstants - ) GetExtractedEnums() - { - var ineligibleConstants = new HashSet(); - var extractedConstants = new HashSet(); - var extractedEnums = new Dictionary< - string, - (EnumDeclarationSyntax, HashSet, HashSet) - >(_numericTypeNames.Count); - - // Try and find constants for each of the enums we've found. - // We do this in descending length order to ensure that we find the longest match for constant names to enum - // names. - foreach ( - var (enumName, enumType) in _numericTypeNames.OrderByDescending(x => x.Key.Length) - ) - { - var enumTrimmingName = NameSplitter.Underscore(enumName); - (EnumDeclarationSyntax, HashSet, HashSet)? extractedEnum = enumType - is { } theType - ? ( - EnumDeclaration(enumName) - .AddBaseListTypes(SimpleBaseType(PredefinedType(Token(theType.Type)))) - .AddModifiers(Token(SyntaxKind.PublicKeyword)), - theType.ReferencingFileDirs, - theType.ReferencingNamespaces - ) - : null; - - // Look through all of the constants and see whether they start with our enum name. - foreach (var (constant, value) in _constants) - { - // We want to account for PascalCase vs SCREAMING_SNAKE_CASE differences (for example) so we do - // four passes (for each combination of the original name vs trimming name, the latter of which - // taking casing into account). It is possible that this could be expanded, but this should be done - // carefully to ensure we don't light up prematurely. - var nextConst = false; - var trimmingName = NameSplitter.Underscore(constant); - foreach ( - var enumCandidate in (ReadOnlySpan)[enumName, enumTrimmingName] - ) - { - foreach ( - var constCandidate in (ReadOnlySpan)[constant, trimmingName] - ) - { - // Make sure the constant name starts with the enum name, and that there is clearly a word - // gap after the enum name in the constant name e.g. API_BlendOp doesn't pull in - // API_BLEND_OPAQUE but it does pull in API_BLEND_OP_ADD (or API_BLENDOP_ADD). - // I wouldn't feel safe relaxing this right now, despite there being obvious use cases. - // Perhaps as a future improvement we can try to walk back the enum's trimming name, check - // that there are no other enums that conflict with that shorter trimming name, and then try - // to widen the scope. So for example, if we have found nothing for API_BlendOp then we'd - // just try API_Blend (provided there's no API_BlendFactor, API_Blend, or any other - // conflicts) which would then sweep up API_BLEND_OPAQUE. We'd then keep the original enum - // names to stay in-keeping with the native API. TODO investigate this - if ( - !constCandidate.StartsWith( - enumCandidate, - StringComparison.OrdinalIgnoreCase - ) - || ( - constCandidate[enumCandidate.Length] != '_' - && char.IsUpper(constCandidate[enumCandidate.Length - 1]) - == char.IsUpper(constCandidate[enumCandidate.Length]) - ) - ) - { - continue; - } - - // We don't generate enums that have had inconsistent usage (e.g. int vs short vs long) but - // if we are able to map constants into those enums, we still want to ensure we don't go and - // map it to a less relevant enum as a result. So we add the constant to a separate HashSet, - // with which we remove the constant from the _constants dictionary but don't return it to - // the Rewriter, ensuring it is not removed (at is has not been mapped to an eligible enum). - (enumType is null ? ineligibleConstants : extractedConstants).Add( - constant - ); - nextConst = true; - if (extractedEnum is not { } theExtractedEnum) - { - break; - } - - theExtractedEnum.Item1 = theExtractedEnum.Item1.AddMembers( - EnumMemberDeclaration(constant) - .WithEqualsValue(EqualsValueClause(value)) - ); - extractedEnum = theExtractedEnum; - break; - } - - if (nextConst) - { - break; - } - } - } - - // Remove the constants that we've mapped into enums - foreach ( - var constant in (IEnumerable) - [.. ineligibleConstants, .. extractedConstants] - ) - { - _constants.Remove(constant); - } - - ineligibleConstants.Clear(); - if (extractedEnum is { Item1.Members.Count: > 0 }) - { - extractedEnums[enumName] = extractedEnum.Value; - } - } - - return (extractedEnums, extractedConstants); - } - - public override SyntaxNode? VisitFieldDeclaration(FieldDeclarationSyntax node) - { - if (node.Modifiers.Any(SyntaxKind.ConstKeyword)) - { - foreach (var vardec in node.Declaration.Variables) - { - if (vardec.Initializer is null) - { - continue; - } - - _constants.Add(vardec.Identifier.ToString(), vardec.Initializer.Value); - } - } - return base.VisitFieldDeclaration(node); - } - - public override SyntaxNode? VisitEnumDeclaration(EnumDeclarationSyntax node) - { - InvalidateIfSeen(_numericTypeNames, node.Identifier.ToString()); - return base.VisitEnumDeclaration(node); - } - } - - private static ( - StructDeclarationSyntax Pfn, - DelegateDeclarationSyntax Delegate - ) CreateFunctionPointerTypes( - string pfnName, - string delegateName, - SyntaxList pfnAttrLists, - SyntaxList delegateAttrLists, - FunctionPointerTypeSyntax rawPfn - ) - { - // Ported from https://github.com/dotnet/Silk.NET/blob/d30cc43b/src/Core/Silk.NET.BuildTools/Bind/StructWriter.cs#L744-L774 - var pfn = StructDeclaration(pfnName) - .WithModifiers( - TokenList( - Token(SyntaxKind.PublicKeyword), - Token(SyntaxKind.UnsafeKeyword), - Token(SyntaxKind.ReadOnlyKeyword) - ) - ) - .WithBaseList( - BaseList( - SingletonSeparatedList( - SimpleBaseType(IdentifierName("IDisposable")) - ) - ) - ) - .WithAttributeLists(pfnAttrLists) - .WithMembers( - List( - [ - FieldDeclaration( - VariableDeclaration( - PointerType(PredefinedType(Token(SyntaxKind.VoidKeyword))), - SingletonSeparatedList(VariableDeclarator("_pointer")) - ) - ) - .WithModifiers( - TokenList( - Token(SyntaxKind.PrivateKeyword), - Token(SyntaxKind.ReadOnlyKeyword) - ) - ), - PropertyDeclaration(rawPfn, "Handle") - .WithModifiers(TokenList(Token(SyntaxKind.PublicKeyword))) - .WithExpressionBody( - ArrowExpressionClause( - CastExpression(rawPfn, IdentifierName("_pointer")) - ) - ) - .WithSemicolonToken(Token(SyntaxKind.SemicolonToken)), - ConstructorDeclaration(pfnName) - .WithParameterList( - ParameterList( - SingletonSeparatedList( - Parameter(Identifier("ptr")).WithType(rawPfn) - ) - ) - ) - .WithExpressionBody( - ArrowExpressionClause( - AssignmentExpression( - SyntaxKind.SimpleAssignmentExpression, - IdentifierName("_pointer"), - IdentifierName("ptr") - ) - ) - ) - .WithModifiers(TokenList(Token(SyntaxKind.PublicKeyword))) - .WithSemicolonToken(Token(SyntaxKind.SemicolonToken)), - ConstructorDeclaration(pfnName) - .WithParameterList( - ParameterList( - SingletonSeparatedList( - Parameter(Identifier("proc")) - .WithType(IdentifierName(delegateName)) - ) - ) - ) - .WithExpressionBody( - ArrowExpressionClause( - AssignmentExpression( - SyntaxKind.SimpleAssignmentExpression, - IdentifierName("_pointer"), - InvocationExpression( - MemberAccessExpression( - SyntaxKind.SimpleMemberAccessExpression, - IdentifierName("SilkMarshal"), - IdentifierName("DelegateToPtr") - ), - ArgumentList( - SingletonSeparatedList( - Argument(IdentifierName("proc")) - ) - ) - ) - ) - ) - ) - .WithModifiers(TokenList(Token(SyntaxKind.PublicKeyword))) - .WithSemicolonToken(Token(SyntaxKind.SemicolonToken)), - MethodDeclaration(PredefinedType(Token(SyntaxKind.VoidKeyword)), "Dispose") - .WithExpressionBody( - ArrowExpressionClause( - InvocationExpression( - MemberAccessExpression( - SyntaxKind.SimpleMemberAccessExpression, - IdentifierName("SilkMarshal"), - IdentifierName("Free") - ), - ArgumentList( - SingletonSeparatedList( - Argument(IdentifierName("_pointer")) - ) - ) - ) - ) - ) - .WithModifiers(TokenList(Token(SyntaxKind.PublicKeyword))) - .WithSemicolonToken(Token(SyntaxKind.SemicolonToken)), - ConversionOperatorDeclaration( - Token(SyntaxKind.ImplicitKeyword), - IdentifierName(pfnName) - ) - .WithParameterList( - ParameterList( - SingletonSeparatedList( - Parameter(Identifier("pfn")).WithType(rawPfn) - ) - ) - ) - .WithModifiers( - TokenList( - Token(SyntaxKind.PublicKeyword), - Token(SyntaxKind.StaticKeyword) - ) - ) - .WithExpressionBody( - ArrowExpressionClause( - ImplicitObjectCreationExpression( - ArgumentList( - SingletonSeparatedList(Argument(IdentifierName("pfn"))) - ), - null - ) - ) - ) - .WithSemicolonToken(Token(SyntaxKind.SemicolonToken)), - ConversionOperatorDeclaration(Token(SyntaxKind.ImplicitKeyword), rawPfn) - .WithParameterList( - ParameterList( - SingletonSeparatedList( - Parameter(Identifier("pfn")) - .WithType(IdentifierName(pfnName)) - ) - ) - ) - .WithModifiers( - TokenList( - Token(SyntaxKind.PublicKeyword), - Token(SyntaxKind.StaticKeyword) - ) - ) - .WithExpressionBody( - ArrowExpressionClause( - CastExpression( - rawPfn, - MemberAccessExpression( - SyntaxKind.SimpleMemberAccessExpression, - IdentifierName("pfn"), - IdentifierName("_pointer") - ) - ) - ) - ) - .WithSemicolonToken(Token(SyntaxKind.SemicolonToken)), - // TODO invoke method? - ] - ) - ); - - var @delegate = DelegateDeclaration( - rawPfn.ParameterList.Parameters.Last().Type, - Identifier(delegateName) - ) - .WithModifiers( - TokenList(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.UnsafeKeyword)) - ) - .WithAttributeLists(delegateAttrLists) - .WithParameterList( - ParameterList( - SeparatedList( - rawPfn - .ParameterList.Parameters.SkipLast(1) - .Select( - (y, i) => - Parameter( - y.AttributeLists, - y.Modifiers, - y.Type, - Identifier($"arg{i}"), - null - ) - ) - ) - ) - ); - return (pfn, @delegate); } } From 298c3b03c9dc154fb60b5c383880ed3446d19edb Mon Sep 17 00:00:00 2001 From: William Chen Date: Mon, 8 Jun 2026 03:05:38 -0400 Subject: [PATCH 05/37] Continue to move code to their new locations --- .../SilkTouch/Mods/ExtractEnumConstants.cs | 46 +- .../SilkTouch/Mods/ExtractFunctionPointers.cs | 522 ++++++++++-------- .../SilkTouch/Mods/ExtractNestedTyping.cs | 102 +--- 3 files changed, 331 insertions(+), 339 deletions(-) diff --git a/sources/SilkTouch/SilkTouch/Mods/ExtractEnumConstants.cs b/sources/SilkTouch/SilkTouch/Mods/ExtractEnumConstants.cs index ff1f0a25ae..ff3ad87aa8 100644 --- a/sources/SilkTouch/SilkTouch/Mods/ExtractEnumConstants.cs +++ b/sources/SilkTouch/SilkTouch/Mods/ExtractEnumConstants.cs @@ -47,7 +47,7 @@ public override async Task ExecuteAsync(IModContext ctx, CancellationToken ct = walker.Visit(node); } - var rewriter = new Rewriter(logger); + var rewriter = new Rewriter(); var (enums, constants) = walker.GetExtractedEnums(); rewriter.ConstantsToRemove = constants; rewriter.ExtractedEnums = enums.Keys; @@ -55,28 +55,6 @@ public override async Task ExecuteAsync(IModContext ctx, CancellationToken ct = ctx.SourceProject = project; } - private partial class Rewriter(ILogger logger) : CSharpSyntaxRewriter - { - public IReadOnlyCollection? ConstantsToRemove { get; set; } - - public IReadOnlyCollection? ExtractedEnums { get; set; } - - public override SyntaxNode? VisitFieldDeclaration(FieldDeclarationSyntax node) - { - var ret = base.VisitFieldDeclaration(node) as FieldDeclarationSyntax; - return ret?.Declaration.Variables.Count == 0 ? null : ret; - } - - public override SyntaxNode? VisitVariableDeclarator(VariableDeclaratorSyntax node) - { - if (ConstantsToRemove?.Contains(node.Identifier.ToString()) ?? false) - { - return null; - } - return base.VisitVariableDeclarator(node); - } - } - private class Walker : CSharpSyntaxRewriter { private readonly Dictionary< @@ -291,4 +269,26 @@ var constant in (IEnumerable) return base.VisitEnumDeclaration(node); } } + + private partial class Rewriter : CSharpSyntaxRewriter + { + public IReadOnlyCollection? ConstantsToRemove { get; set; } + + public IReadOnlyCollection? ExtractedEnums { get; set; } + + public override SyntaxNode? VisitFieldDeclaration(FieldDeclarationSyntax node) + { + var ret = base.VisitFieldDeclaration(node) as FieldDeclarationSyntax; + return ret?.Declaration.Variables.Count == 0 ? null : ret; + } + + public override SyntaxNode? VisitVariableDeclarator(VariableDeclaratorSyntax node) + { + if (ConstantsToRemove?.Contains(node.Identifier.ToString()) ?? false) + { + return null; + } + return base.VisitVariableDeclarator(node); + } + } } diff --git a/sources/SilkTouch/SilkTouch/Mods/ExtractFunctionPointers.cs b/sources/SilkTouch/SilkTouch/Mods/ExtractFunctionPointers.cs index 517bda842d..b4a0eb67a9 100644 --- a/sources/SilkTouch/SilkTouch/Mods/ExtractFunctionPointers.cs +++ b/sources/SilkTouch/SilkTouch/Mods/ExtractFunctionPointers.cs @@ -1,12 +1,14 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics; using System.Text.RegularExpressions; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.Extensions.Logging; using Silk.NET.SilkTouch.Naming; +using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; namespace Silk.NET.SilkTouch.Mods; @@ -14,7 +16,7 @@ namespace Silk.NET.SilkTouch.Mods; /// Replaces function pointers identified by their s /// with delegates and function pointer structs. /// -public partial class ExtractFunctionPointers : Mod +public partial class ExtractFunctionPointers(ILogger logger) : Mod { /// public override async Task ExecuteAsync(IModContext ctx, CancellationToken ct = default) @@ -43,6 +45,7 @@ public override async Task ExecuteAsync(IModContext ctx, CancellationToken ct = // Add documents for each extracted function pointer // This is moved out of the foreach statement for better debuggability + var rewriter = new Rewriter(logger); var extractedFunctionPointers = rewriter .FunctionPointerTypes.Values // .Where(x => x.IsUnique) @@ -103,235 +106,65 @@ public override async Task ExecuteAsync(IModContext ctx, CancellationToken ct = ctx.SourceProject = project; } - private partial class Rewriter(ILogger logger) : CSharpSyntaxRewriter + private static ReadOnlySpan GetNativeTypeNameForPredefinedType( + PredefinedTypeSyntax node, + Dictionary, HashSet)?>? numericTypeNames = null + ) { - private string? _typeNameFromOuterFunctionPointer; - private string? _fallbackFromOuterFunctionPointer; - - public Dictionary< - string, - ( - StructDeclarationSyntax Pfn, - DelegateDeclarationSyntax Delegate, - HashSet ReferencingFileDirs, - HashSet ReferencingNamespaces - ) - > FunctionPointerTypes { get; set; } = []; - - public override SyntaxNode? VisitFunctionPointerType(FunctionPointerTypeSyntax node) + // Walk up to the parameter or method. We only allow primitive integer types right now. + var current = node.Parent; + var indirectionLevels = 0; + while (current is PointerTypeSyntax) { - // Walk up the type. We expect only pointers above us, but we could encounter a function pointer type in - // which case we just ignore all this as we should already have a _currentNativeTypeName. Anything else and - // we don't have enough context for a fallback. - var current = node.Parent; - var indirectionLevels = 0; - while (current is PointerTypeSyntax) - { - indirectionLevels++; - current = current.Parent; - } - - // As above, get the native type name if we can and also get a fallback name based on context. - var (currentNativeTypeName, fallback) = current switch - { - MethodDeclarationSyntax meth => ( - meth.AttributeLists.GetNativeTypeName(SyntaxKind.ReturnKeyword), - $"{meth.Identifier}_r" - ), - ParameterSyntax { Parent.Parent: MethodDeclarationSyntax meth } param => ( - param.AttributeLists.GetNativeTypeName(), - $"{meth.Identifier}_{param.Identifier}" - ), - VariableDeclarationSyntax - { - Parent: FieldDeclarationSyntax { Parent: BaseTypeDeclarationSyntax type } fld - } vardec => ( - fld.AttributeLists.GetNativeTypeName(), - $"{type.Identifier}_{vardec.Variables[0].Identifier}" - ), - _ => (null, null), - }; - - // If the native type name is actually the function pointer signature (i.e. not through a typedef) then we - // should pass the native type name down when recursing. - fallback = _fallbackFromOuterFunctionPointer ?? fallback; - currentNativeTypeName = - (_typeNameFromOuterFunctionPointer ?? currentNativeTypeName)?.Trim() ?? fallback; - string[]? recursiveTypeNames = null; - if (currentNativeTypeName.AsSpan().ContainsAnyExcept(NameUtils.IdentifierChars)) - { - var match = FunctionPointerNativeTypeNameRegex().Match(currentNativeTypeName!); - if (match.Success) - { - currentNativeTypeName = fallback; - - // NOTE: We expect the groups to be as follows: - // 0 = everything - // 1 = return type - // 2 = indirection levels + 1 - // 3 = comma separated parameter types - recursiveTypeNames = new string[ - 1 - + (match.Groups[3].Value.Length > 0 ? 1 : 0) - + match.Groups[3].Value.AsSpan().Count(',') - ]; - if (match.Groups[2].Value.AsSpan().Count('*') != indirectionLevels + 1) - { - logger.LogWarning( - "Unable to deal with function pointer usage at {} - mismatch of indirection " - + "levels: {} for {}", - node.GetLocation().GetLineSpan(), - node, - currentNativeTypeName - ); - return node; - } - - recursiveTypeNames[^1] = match.Groups[1].Value; - var @params = match - .Groups[3] - .Value.Split( - ',', - StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries - ); - for (var i = 0; i < @params.Length; i++) - { - recursiveTypeNames[i] = @params[i]; - } - } - else - { - // Maybe it's a pointer type? - var idSpan = currentNativeTypeName.AsSpan(); - if (idSpan.StartsWith("const ")) - { - idSpan = idSpan["const ".Length..]; - } - - // If the indirection levels match (and the only other non-identifier characters are whitespace) - // then we can use the identifier as the native name. - idSpan = idSpan.Trim(); - var badStart = idSpan.IndexOfAnyExcept(NameUtils.IdentifierChars); - var bad = idSpan[badStart..]; - currentNativeTypeName = - badStart == -1 - || ( - bad.Count('*') == indirectionLevels - && bad.Count(' ') == bad.Length - indirectionLevels - ) - ? idSpan[..badStart].ToString() - : fallback; - } - } + indirectionLevels++; + current = current.Parent; + } - if (currentNativeTypeName is null) - { - logger.LogWarning( - "Unable to deal with function pointer usage at {} - terminated at {}: {}", - node.GetLocation().GetLineSpan(), - current?.GetType().Name ?? "null", - current - ); - return node; - } + var attrs = current switch + { + MethodDeclarationSyntax meth => meth.AttributeLists, + ParameterSyntax param => param.AttributeLists, + _ => default, + }; - // Assert that our state is valid given the tests we've done above before recursing. - Debug.Assert( - _fallbackFromOuterFunctionPointer is not null - == node.Ancestors().OfType().Any() - ); + if (attrs.Count == 0) + { + return default; + } - // Ensure that we've recursively generated and fixed up any function pointers contained within this function - // pointer. - var ns = node.NamespaceFromSyntaxNode(); - node = node.WithParameterList( - node.ParameterList.WithParameters( - SyntaxFactory.SeparatedList( - node.ParameterList.Parameters.Select( - (x, i) => - { - var typeNameBefore = _typeNameFromOuterFunctionPointer; - var fallbackBefore = _fallbackFromOuterFunctionPointer; - _typeNameFromOuterFunctionPointer = recursiveTypeNames?[i]; - _fallbackFromOuterFunctionPointer = - $"{currentNativeTypeName}_p{i}"; - var ret = base.Visit(x); - _typeNameFromOuterFunctionPointer = typeNameBefore; - _fallbackFromOuterFunctionPointer = fallbackBefore; - return ret; - } - ) - .OfType() - ) - ) - ); + if (!attrs.TryParseNativeTypeName(out var info)) + { + return null; + } - // Generate the types if we haven't already. - if (!FunctionPointerTypes.TryGetValue(currentNativeTypeName, out var pfnInfo)) - { - var (pfn, @delegate) = CreateFunctionPointerTypes( - currentNativeTypeName, - $"{currentNativeTypeName}Delegate", - ( - currentNativeTypeName == fallback - ? SyntaxFactory.SingletonList( - SyntaxFactory.AttributeList( - SyntaxFactory.SingletonSeparatedList( - SyntaxFactory.Attribute( - SyntaxFactory.IdentifierName("Transformed") - ) - ) - ) - ) - : default - ).WithNativeName(currentNativeTypeName), - ( - currentNativeTypeName == fallback - ? SyntaxFactory.SingletonList( - SyntaxFactory.AttributeList( - SyntaxFactory.SingletonSeparatedList( - SyntaxFactory.Attribute( - SyntaxFactory.IdentifierName("Transformed") - ) - ) - ) - ) - : default - ) - .WithNativeName(currentNativeTypeName) - .AddReferencedNameAffix( - NameAffixType.Prefix, - "FunctionPointerParent", - currentNativeTypeName - ) - .AddNameAffix( - NameAffixType.Suffix, - "FunctionPointerDelegateType", - "Delegate" - ), - node - ); - FunctionPointerTypes[currentNativeTypeName] = pfnInfo = (pfn, @delegate, [], []); - } + // Ensure that the indirection levels indicated by the type name is the same as we've encountered when walking + // up the type. If this isn't, this indicates that the native type name is a typedef to a pointer and shouldn't + // be something that is mapped into an enum. + if (info.IndirectionLevels == indirectionLevels) + { + return info.Name; + } - // Ensure this visitation is used to determine the namespace/location. - pfnInfo.ReferencingNamespaces.Add(ns); - if (File?[..File.LastIndexOf('/')] is { } dir) - { - pfnInfo.ReferencingFileDirs.Add(dir); - } + InvalidateIfSeen(numericTypeNames, info.Name); + return null; + } - return SyntaxFactory.IdentifierName(currentNativeTypeName); + private static void InvalidateIfSeen( + Dictionary, HashSet)?>? numericTypeNames, + string nativeTypeName + ) + { + if (numericTypeNames?.ContainsKey(nativeTypeName) ?? false) + { + numericTypeNames[nativeTypeName] = null; } - - [GeneratedRegex( - @"^((?:[A-Za-z0-9\s\*_]|\[[0-9]*\])+)\((\*)+\)\(((?:(?:[A-Za-z0-9\s\*_]|\[[0-9]*\])+,?)*)\)" - )] - private partial Regex FunctionPointerNativeTypeNameRegex(); } private class Walker : CSharpSyntaxRewriter { + public string? Namespace { get; set; } + public string? File { get; set; } + private static ( StructDeclarationSyntax Pfn, DelegateDeclarationSyntax Delegate @@ -605,4 +438,259 @@ FunctionPointerTypeSyntax rawPfn return (pfn, @delegate); } } + + private partial class Rewriter(ILogger logger) : CSharpSyntaxRewriter + { + private Dictionary _typeRenames = []; + + private string? _typeNameFromOuterFunctionPointer; + private string? _fallbackFromOuterFunctionPointer; + + public Dictionary< + string, + ( + StructDeclarationSyntax Pfn, + DelegateDeclarationSyntax Delegate, + HashSet ReferencingFileDirs, + HashSet ReferencingNamespaces + ) + > FunctionPointerTypes { get; set; } = []; + + public override SyntaxNode? VisitIdentifierName(IdentifierNameSyntax node) => + base.VisitIdentifierName( + _typeRenames.TryGetValue(node.Identifier.ToString(), out var v) + || ( + v = + FunctionPointerTypes?.TryGetValue(node.Identifier.ToString(), out var pfni) + ?? false + ? pfni.Pfn.Identifier.ToString() + : null + ) + is not null + ? node.WithIdentifier(Identifier(v)) + : node + ); + + public override SyntaxNode? VisitPredefinedType(PredefinedTypeSyntax node) + { + var nativeTypeName = GetNativeTypeNameForPredefinedType(node).ToString(); + if (ExtractedEnums?.Contains(nativeTypeName) ?? false) + { + return IdentifierName(nativeTypeName).WithTriviaFrom(node); + } + + return base.VisitPredefinedType(node); + } + + public override SyntaxNode? VisitFunctionPointerType(FunctionPointerTypeSyntax node) + { + // Walk up the type. We expect only pointers above us, but we could encounter a function pointer type in + // which case we just ignore all this as we should already have a _currentNativeTypeName. Anything else and + // we don't have enough context for a fallback. + var current = node.Parent; + var indirectionLevels = 0; + while (current is PointerTypeSyntax) + { + indirectionLevels++; + current = current.Parent; + } + + // As above, get the native type name if we can and also get a fallback name based on context. + var (currentNativeTypeName, fallback) = current switch + { + MethodDeclarationSyntax meth => ( + meth.AttributeLists.GetNativeTypeName(SyntaxKind.ReturnKeyword), + $"{meth.Identifier}_r" + ), + ParameterSyntax { Parent.Parent: MethodDeclarationSyntax meth } param => ( + param.AttributeLists.GetNativeTypeName(), + $"{meth.Identifier}_{param.Identifier}" + ), + VariableDeclarationSyntax + { + Parent: FieldDeclarationSyntax { Parent: BaseTypeDeclarationSyntax type } fld + } vardec => ( + fld.AttributeLists.GetNativeTypeName(), + $"{type.Identifier}_{vardec.Variables[0].Identifier}" + ), + _ => (null, null), + }; + + // If the native type name is actually the function pointer signature (i.e. not through a typedef) then we + // should pass the native type name down when recursing. + fallback = _fallbackFromOuterFunctionPointer ?? fallback; + currentNativeTypeName = + (_typeNameFromOuterFunctionPointer ?? currentNativeTypeName)?.Trim() ?? fallback; + string[]? recursiveTypeNames = null; + if (currentNativeTypeName.AsSpan().ContainsAnyExcept(NameUtils.IdentifierChars)) + { + var match = FunctionPointerNativeTypeNameRegex().Match(currentNativeTypeName!); + if (match.Success) + { + currentNativeTypeName = fallback; + + // NOTE: We expect the groups to be as follows: + // 0 = everything + // 1 = return type + // 2 = indirection levels + 1 + // 3 = comma separated parameter types + recursiveTypeNames = new string[ + 1 + + (match.Groups[3].Value.Length > 0 ? 1 : 0) + + match.Groups[3].Value.AsSpan().Count(',') + ]; + if (match.Groups[2].Value.AsSpan().Count('*') != indirectionLevels + 1) + { + logger.LogWarning( + "Unable to deal with function pointer usage at {} - mismatch of indirection " + + "levels: {} for {}", + node.GetLocation().GetLineSpan(), + node, + currentNativeTypeName + ); + return node; + } + + recursiveTypeNames[^1] = match.Groups[1].Value; + var @params = match + .Groups[3] + .Value.Split( + ',', + StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries + ); + for (var i = 0; i < @params.Length; i++) + { + recursiveTypeNames[i] = @params[i]; + } + } + else + { + // Maybe it's a pointer type? + var idSpan = currentNativeTypeName.AsSpan(); + if (idSpan.StartsWith("const ")) + { + idSpan = idSpan["const ".Length..]; + } + + // If the indirection levels match (and the only other non-identifier characters are whitespace) + // then we can use the identifier as the native name. + idSpan = idSpan.Trim(); + var badStart = idSpan.IndexOfAnyExcept(NameUtils.IdentifierChars); + var bad = idSpan[badStart..]; + currentNativeTypeName = + badStart == -1 + || ( + bad.Count('*') == indirectionLevels + && bad.Count(' ') == bad.Length - indirectionLevels + ) + ? idSpan[..badStart].ToString() + : fallback; + } + } + + if (currentNativeTypeName is null) + { + logger.LogWarning( + "Unable to deal with function pointer usage at {} - terminated at {}: {}", + node.GetLocation().GetLineSpan(), + current?.GetType().Name ?? "null", + current + ); + return node; + } + + // Assert that our state is valid given the tests we've done above before recursing. + Debug.Assert( + _fallbackFromOuterFunctionPointer is not null + == node.Ancestors().OfType().Any() + ); + + // Ensure that we've recursively generated and fixed up any function pointers contained within this function + // pointer. + var ns = node.NamespaceFromSyntaxNode(); + node = node.WithParameterList( + node.ParameterList.WithParameters( + SyntaxFactory.SeparatedList( + node.ParameterList.Parameters.Select( + (x, i) => + { + var typeNameBefore = _typeNameFromOuterFunctionPointer; + var fallbackBefore = _fallbackFromOuterFunctionPointer; + _typeNameFromOuterFunctionPointer = recursiveTypeNames?[i]; + _fallbackFromOuterFunctionPointer = + $"{currentNativeTypeName}_p{i}"; + var ret = base.Visit(x); + _typeNameFromOuterFunctionPointer = typeNameBefore; + _fallbackFromOuterFunctionPointer = fallbackBefore; + return ret; + } + ) + .OfType() + ) + ) + ); + + // Generate the types if we haven't already. + if (!FunctionPointerTypes.TryGetValue(currentNativeTypeName, out var pfnInfo)) + { + var (pfn, @delegate) = CreateFunctionPointerTypes( + currentNativeTypeName, + $"{currentNativeTypeName}Delegate", + ( + currentNativeTypeName == fallback + ? SyntaxFactory.SingletonList( + SyntaxFactory.AttributeList( + SyntaxFactory.SingletonSeparatedList( + SyntaxFactory.Attribute( + SyntaxFactory.IdentifierName("Transformed") + ) + ) + ) + ) + : default + ).WithNativeName(currentNativeTypeName), + ( + currentNativeTypeName == fallback + ? SyntaxFactory.SingletonList( + SyntaxFactory.AttributeList( + SyntaxFactory.SingletonSeparatedList( + SyntaxFactory.Attribute( + SyntaxFactory.IdentifierName("Transformed") + ) + ) + ) + ) + : default + ) + .WithNativeName(currentNativeTypeName) + .AddReferencedNameAffix( + NameAffixType.Prefix, + "FunctionPointerParent", + currentNativeTypeName + ) + .AddNameAffix( + NameAffixType.Suffix, + "FunctionPointerDelegateType", + "Delegate" + ), + node + ); + FunctionPointerTypes[currentNativeTypeName] = pfnInfo = (pfn, @delegate, [], []); + } + + // Ensure this visitation is used to determine the namespace/location. + pfnInfo.ReferencingNamespaces.Add(ns); + if (File?[..File.LastIndexOf('/')] is { } dir) + { + pfnInfo.ReferencingFileDirs.Add(dir); + } + + return SyntaxFactory.IdentifierName(currentNativeTypeName); + } + + [GeneratedRegex( + @"^((?:[A-Za-z0-9\s\*_]|\[[0-9]*\])+)\((\*)+\)\(((?:(?:[A-Za-z0-9\s\*_]|\[[0-9]*\])+,?)*)\)" + )] + private partial Regex FunctionPointerNativeTypeNameRegex(); + } } diff --git a/sources/SilkTouch/SilkTouch/Mods/ExtractNestedTyping.cs b/sources/SilkTouch/SilkTouch/Mods/ExtractNestedTyping.cs index ae8d690c78..3a3ffb90df 100644 --- a/sources/SilkTouch/SilkTouch/Mods/ExtractNestedTyping.cs +++ b/sources/SilkTouch/SilkTouch/Mods/ExtractNestedTyping.cs @@ -5,7 +5,6 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.Extensions.Logging; using Silk.NET.SilkTouch.Clang; using Silk.NET.SilkTouch.Naming; using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; @@ -16,7 +15,7 @@ namespace Silk.NET.SilkTouch.Mods; /// Extracts nested types into their own separate types. /// In particular, this also handles fixed buffers and anonymous structures output by . /// -public partial class ExtractNestedTyping(ILogger logger) : Mod +public partial class ExtractNestedTyping : Mod { /// public override async Task ExecuteAsync(IModContext ctx, CancellationToken ct = default) @@ -29,22 +28,7 @@ public override async Task ExecuteAsync(IModContext ctx, CancellationToken ct = return; } - // First pass to gather data, such as the types to extract and generate - var walker = new Walker(); - foreach (var doc in project.Documents) - { - var (fname, node) = (doc.RelativePath(), await doc.GetSyntaxRootAsync(ct)); - if (fname is null) - { - continue; - } - - walker.File = fname; - walker.Visit(node); - } - - // Second pass to modify existing files as per our discovery. - var rewriter = new Rewriter(logger); + var rewriter = new Rewriter(); foreach (var docId in project.DocumentIds) { var doc = @@ -104,61 +88,7 @@ rewriter.Namespace is not null ctx.SourceProject = project; } - private static ReadOnlySpan GetNativeTypeNameForPredefinedType( - PredefinedTypeSyntax node, - Dictionary, HashSet)?>? numericTypeNames = null - ) - { - // Walk up to the parameter or method. We only allow primitive integer types right now. - var current = node.Parent; - var indirectionLevels = 0; - while (current is PointerTypeSyntax) - { - indirectionLevels++; - current = current.Parent; - } - - var attrs = current switch - { - MethodDeclarationSyntax meth => meth.AttributeLists, - ParameterSyntax param => param.AttributeLists, - _ => default, - }; - - if (attrs.Count == 0) - { - return default; - } - - if (!attrs.TryParseNativeTypeName(out var info)) - { - return null; - } - - // Ensure that the indirection levels indicated by the type name is the same as we've encountered when walking - // up the type. If this isn't, this indicates that the native type name is a typedef to a pointer and shouldn't - // be something that is mapped into an enum. - if (info.IndirectionLevels == indirectionLevels) - { - return info.Name; - } - - InvalidateIfSeen(numericTypeNames, info.Name); - return null; - } - - private static void InvalidateIfSeen( - Dictionary, HashSet)?>? numericTypeNames, - string nativeTypeName - ) - { - if (numericTypeNames?.ContainsKey(nativeTypeName) ?? false) - { - numericTypeNames[nativeTypeName] = null; - } - } - - private partial class Rewriter(ILogger logger) : CSharpSyntaxRewriter + private partial class Rewriter : CSharpSyntaxRewriter { private Dictionary _typeRenames = []; @@ -167,32 +97,6 @@ private partial class Rewriter(ILogger logger) : CSharpSyntaxRewriter public string? Namespace { get; set; } public string? File { get; set; } - public override SyntaxNode? VisitIdentifierName(IdentifierNameSyntax node) => - base.VisitIdentifierName( - _typeRenames.TryGetValue(node.Identifier.ToString(), out var v) - || ( - v = - FunctionPointerTypes?.TryGetValue(node.Identifier.ToString(), out var pfni) - ?? false - ? pfni.Pfn.Identifier.ToString() - : null - ) - is not null - ? node.WithIdentifier(Identifier(v)) - : node - ); - - public override SyntaxNode? VisitPredefinedType(PredefinedTypeSyntax node) - { - var nativeTypeName = GetNativeTypeNameForPredefinedType(node).ToString(); - if (ExtractedEnums?.Contains(nativeTypeName) ?? false) - { - return IdentifierName(nativeTypeName).WithTriviaFrom(node); - } - - return base.VisitPredefinedType(node); - } - public override SyntaxNode? VisitStructDeclaration(StructDeclarationSyntax node) { // Extract nested structs From e9b67f969f9871b9722debdb7d2812d3db249c2c Mon Sep 17 00:00:00 2001 From: William Chen Date: Mon, 8 Jun 2026 03:08:40 -0400 Subject: [PATCH 06/37] Further cleanup --- .../SilkTouch/Mods/ExtractEnumConstants.cs | 6 +- .../SilkTouch/Mods/ExtractFunctionPointers.cs | 288 +++++++----------- .../SilkTouch/Mods/ExtractNestedTyping.cs | 2 +- 3 files changed, 114 insertions(+), 182 deletions(-) diff --git a/sources/SilkTouch/SilkTouch/Mods/ExtractEnumConstants.cs b/sources/SilkTouch/SilkTouch/Mods/ExtractEnumConstants.cs index ff3ad87aa8..3ea337ab4c 100644 --- a/sources/SilkTouch/SilkTouch/Mods/ExtractEnumConstants.cs +++ b/sources/SilkTouch/SilkTouch/Mods/ExtractEnumConstants.cs @@ -4,7 +4,6 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.Extensions.Logging; using Silk.NET.SilkTouch.Naming; namespace Silk.NET.SilkTouch.Mods; @@ -20,7 +19,7 @@ namespace Silk.NET.SilkTouch.Mods; /// extern MyEnum GetMyEnum(); /// /// -public partial class ExtractEnumConstants : Mod +public class ExtractEnumConstants : Mod { /// public override async Task ExecuteAsync(IModContext ctx, CancellationToken ct = default) @@ -270,10 +269,9 @@ var constant in (IEnumerable) } } - private partial class Rewriter : CSharpSyntaxRewriter + private class Rewriter : CSharpSyntaxRewriter { public IReadOnlyCollection? ConstantsToRemove { get; set; } - public IReadOnlyCollection? ExtractedEnums { get; set; } public override SyntaxNode? VisitFieldDeclaration(FieldDeclarationSyntax node) diff --git a/sources/SilkTouch/SilkTouch/Mods/ExtractFunctionPointers.cs b/sources/SilkTouch/SilkTouch/Mods/ExtractFunctionPointers.cs index b4a0eb67a9..e7cefc749e 100644 --- a/sources/SilkTouch/SilkTouch/Mods/ExtractFunctionPointers.cs +++ b/sources/SilkTouch/SilkTouch/Mods/ExtractFunctionPointers.cs @@ -66,7 +66,7 @@ public override async Task ExecuteAsync(IModContext ctx, CancellationToken ct = ), ] ) - .Concat( + .Concat( // TODO: Looks like I misnamed the variable when I refactored this last year. This handles both enums and function pointers enums.Select(x => ( (MemberDeclarationSyntax)x.Value.Item1, @@ -85,13 +85,11 @@ public override async Task ExecuteAsync(IModContext ctx, CancellationToken ct = project = project ?.AddDocument( $"{identifier}.gen.cs", - SyntaxFactory - .CompilationUnit() + CompilationUnit() .WithMembers( ns is { Length: > 0 } - ? SyntaxFactory.SingletonList( - SyntaxFactory - .FileScopedNamespaceDeclaration( + ? SingletonList( + FileScopedNamespaceDeclaration( ModUtils.NamespaceIntoIdentifierName(ns.TrimEnd('.')) ) .WithMembers(SingletonList(typeDecl)) @@ -162,7 +160,6 @@ string nativeTypeName private class Walker : CSharpSyntaxRewriter { - public string? Namespace { get; set; } public string? File { get; set; } private static ( @@ -177,258 +174,200 @@ FunctionPointerTypeSyntax rawPfn ) { // Ported from https://github.com/dotnet/Silk.NET/blob/d30cc43b/src/Core/Silk.NET.BuildTools/Bind/StructWriter.cs#L744-L774 - var pfn = SyntaxFactory - .StructDeclaration(pfnName) + var pfn = StructDeclaration(pfnName) .WithModifiers( - SyntaxFactory.TokenList( - SyntaxFactory.Token(SyntaxKind.PublicKeyword), - SyntaxFactory.Token(SyntaxKind.UnsafeKeyword), - SyntaxFactory.Token(SyntaxKind.ReadOnlyKeyword) + TokenList( + Token(SyntaxKind.PublicKeyword), + Token(SyntaxKind.UnsafeKeyword), + Token(SyntaxKind.ReadOnlyKeyword) ) ) .WithBaseList( - SyntaxFactory.BaseList( - SyntaxFactory.SingletonSeparatedList( - SyntaxFactory.SimpleBaseType( - SyntaxFactory.IdentifierName("IDisposable") - ) + BaseList( + SingletonSeparatedList( + SimpleBaseType(IdentifierName("IDisposable")) ) ) ) .WithAttributeLists(pfnAttrLists) .WithMembers( - SyntaxFactory.List( + List( [ - SyntaxFactory - .FieldDeclaration( - SyntaxFactory.VariableDeclaration( - SyntaxFactory.PointerType( - SyntaxFactory.PredefinedType( - SyntaxFactory.Token(SyntaxKind.VoidKeyword) - ) - ), - SyntaxFactory.SingletonSeparatedList( - SyntaxFactory.VariableDeclarator("_pointer") - ) + FieldDeclaration( + VariableDeclaration( + PointerType(PredefinedType(Token(SyntaxKind.VoidKeyword))), + SingletonSeparatedList(VariableDeclarator("_pointer")) ) ) .WithModifiers( - SyntaxFactory.TokenList( - SyntaxFactory.Token(SyntaxKind.PrivateKeyword), - SyntaxFactory.Token(SyntaxKind.ReadOnlyKeyword) + TokenList( + Token(SyntaxKind.PrivateKeyword), + Token(SyntaxKind.ReadOnlyKeyword) ) ), - SyntaxFactory - .PropertyDeclaration(rawPfn, "Handle") - .WithModifiers( - SyntaxFactory.TokenList( - SyntaxFactory.Token(SyntaxKind.PublicKeyword) - ) - ) + PropertyDeclaration(rawPfn, "Handle") + .WithModifiers(TokenList(Token(SyntaxKind.PublicKeyword))) .WithExpressionBody( - SyntaxFactory.ArrowExpressionClause( - SyntaxFactory.CastExpression( - rawPfn, - SyntaxFactory.IdentifierName("_pointer") - ) + ArrowExpressionClause( + CastExpression(rawPfn, IdentifierName("_pointer")) ) ) - .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)), - SyntaxFactory - .ConstructorDeclaration(pfnName) + .WithSemicolonToken(Token(SyntaxKind.SemicolonToken)), + ConstructorDeclaration(pfnName) .WithParameterList( - SyntaxFactory.ParameterList( - SyntaxFactory.SingletonSeparatedList( - SyntaxFactory - .Parameter(SyntaxFactory.Identifier("ptr")) - .WithType(rawPfn) + ParameterList( + SingletonSeparatedList( + Parameter(Identifier("ptr")).WithType(rawPfn) ) ) ) .WithExpressionBody( - SyntaxFactory.ArrowExpressionClause( - SyntaxFactory.AssignmentExpression( + ArrowExpressionClause( + AssignmentExpression( SyntaxKind.SimpleAssignmentExpression, - SyntaxFactory.IdentifierName("_pointer"), - SyntaxFactory.IdentifierName("ptr") + IdentifierName("_pointer"), + IdentifierName("ptr") ) ) ) - .WithModifiers( - SyntaxFactory.TokenList( - SyntaxFactory.Token(SyntaxKind.PublicKeyword) - ) - ) - .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)), - SyntaxFactory - .ConstructorDeclaration(pfnName) + .WithModifiers(TokenList(Token(SyntaxKind.PublicKeyword))) + .WithSemicolonToken(Token(SyntaxKind.SemicolonToken)), + ConstructorDeclaration(pfnName) .WithParameterList( - SyntaxFactory.ParameterList( - SyntaxFactory.SingletonSeparatedList( - SyntaxFactory - .Parameter(SyntaxFactory.Identifier("proc")) - .WithType( - SyntaxFactory.IdentifierName(delegateName) - ) + ParameterList( + SingletonSeparatedList( + Parameter(Identifier("proc")) + .WithType(IdentifierName(delegateName)) ) ) ) .WithExpressionBody( - SyntaxFactory.ArrowExpressionClause( - SyntaxFactory.AssignmentExpression( + ArrowExpressionClause( + AssignmentExpression( SyntaxKind.SimpleAssignmentExpression, - SyntaxFactory.IdentifierName("_pointer"), - SyntaxFactory.InvocationExpression( - SyntaxFactory.MemberAccessExpression( + IdentifierName("_pointer"), + InvocationExpression( + MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, - SyntaxFactory.IdentifierName("SilkMarshal"), - SyntaxFactory.IdentifierName("DelegateToPtr") + IdentifierName("SilkMarshal"), + IdentifierName("DelegateToPtr") ), - SyntaxFactory.ArgumentList( - SyntaxFactory.SingletonSeparatedList( - SyntaxFactory.Argument( - SyntaxFactory.IdentifierName("proc") - ) + ArgumentList( + SingletonSeparatedList( + Argument(IdentifierName("proc")) ) ) ) ) ) ) - .WithModifiers( - SyntaxFactory.TokenList( - SyntaxFactory.Token(SyntaxKind.PublicKeyword) - ) - ) - .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)), - SyntaxFactory - .MethodDeclaration( - SyntaxFactory.PredefinedType( - SyntaxFactory.Token(SyntaxKind.VoidKeyword) - ), + .WithModifiers(TokenList(Token(SyntaxKind.PublicKeyword))) + .WithSemicolonToken(Token(SyntaxKind.SemicolonToken)), + MethodDeclaration( + PredefinedType(Token(SyntaxKind.VoidKeyword)), "Dispose" ) .WithExpressionBody( - SyntaxFactory.ArrowExpressionClause( - SyntaxFactory.InvocationExpression( - SyntaxFactory.MemberAccessExpression( + ArrowExpressionClause( + InvocationExpression( + MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, - SyntaxFactory.IdentifierName("SilkMarshal"), - SyntaxFactory.IdentifierName("Free") + IdentifierName("SilkMarshal"), + IdentifierName("Free") ), - SyntaxFactory.ArgumentList( - SyntaxFactory.SingletonSeparatedList( - SyntaxFactory.Argument( - SyntaxFactory.IdentifierName("_pointer") - ) + ArgumentList( + SingletonSeparatedList( + Argument(IdentifierName("_pointer")) ) ) ) ) ) - .WithModifiers( - SyntaxFactory.TokenList( - SyntaxFactory.Token(SyntaxKind.PublicKeyword) - ) - ) - .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)), - SyntaxFactory - .ConversionOperatorDeclaration( - SyntaxFactory.Token(SyntaxKind.ImplicitKeyword), - SyntaxFactory.IdentifierName(pfnName) + .WithModifiers(TokenList(Token(SyntaxKind.PublicKeyword))) + .WithSemicolonToken(Token(SyntaxKind.SemicolonToken)), + ConversionOperatorDeclaration( + Token(SyntaxKind.ImplicitKeyword), + IdentifierName(pfnName) ) .WithParameterList( - SyntaxFactory.ParameterList( - SyntaxFactory.SingletonSeparatedList( - SyntaxFactory - .Parameter(SyntaxFactory.Identifier("pfn")) - .WithType(rawPfn) + ParameterList( + SingletonSeparatedList( + Parameter(Identifier("pfn")).WithType(rawPfn) ) ) ) .WithModifiers( - SyntaxFactory.TokenList( - SyntaxFactory.Token(SyntaxKind.PublicKeyword), - SyntaxFactory.Token(SyntaxKind.StaticKeyword) + TokenList( + Token(SyntaxKind.PublicKeyword), + Token(SyntaxKind.StaticKeyword) ) ) .WithExpressionBody( - SyntaxFactory.ArrowExpressionClause( - SyntaxFactory.ImplicitObjectCreationExpression( - SyntaxFactory.ArgumentList( - SyntaxFactory.SingletonSeparatedList( - SyntaxFactory.Argument( - SyntaxFactory.IdentifierName("pfn") - ) + ArrowExpressionClause( + ImplicitObjectCreationExpression( + ArgumentList( + SingletonSeparatedList( + Argument(IdentifierName("pfn")) ) ), null ) ) ) - .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)), - SyntaxFactory - .ConversionOperatorDeclaration( - SyntaxFactory.Token(SyntaxKind.ImplicitKeyword), - rawPfn - ) + .WithSemicolonToken(Token(SyntaxKind.SemicolonToken)), + ConversionOperatorDeclaration(Token(SyntaxKind.ImplicitKeyword), rawPfn) .WithParameterList( - SyntaxFactory.ParameterList( - SyntaxFactory.SingletonSeparatedList( - SyntaxFactory - .Parameter(SyntaxFactory.Identifier("pfn")) - .WithType(SyntaxFactory.IdentifierName(pfnName)) + ParameterList( + SingletonSeparatedList( + Parameter(Identifier("pfn")) + .WithType(IdentifierName(pfnName)) ) ) ) .WithModifiers( - SyntaxFactory.TokenList( - SyntaxFactory.Token(SyntaxKind.PublicKeyword), - SyntaxFactory.Token(SyntaxKind.StaticKeyword) + TokenList( + Token(SyntaxKind.PublicKeyword), + Token(SyntaxKind.StaticKeyword) ) ) .WithExpressionBody( - SyntaxFactory.ArrowExpressionClause( - SyntaxFactory.CastExpression( + ArrowExpressionClause( + CastExpression( rawPfn, - SyntaxFactory.MemberAccessExpression( + MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, - SyntaxFactory.IdentifierName("pfn"), - SyntaxFactory.IdentifierName("_pointer") + IdentifierName("pfn"), + IdentifierName("_pointer") ) ) ) ) - .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)), + .WithSemicolonToken(Token(SyntaxKind.SemicolonToken)), // TODO invoke method? ] ) ); - var @delegate = SyntaxFactory - .DelegateDeclaration( + var @delegate = DelegateDeclaration( rawPfn.ParameterList.Parameters.Last().Type, - SyntaxFactory.Identifier(delegateName) + Identifier(delegateName) ) .WithModifiers( - SyntaxFactory.TokenList( - SyntaxFactory.Token(SyntaxKind.PublicKeyword), - SyntaxFactory.Token(SyntaxKind.UnsafeKeyword) - ) + TokenList(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.UnsafeKeyword)) ) .WithAttributeLists(delegateAttrLists) .WithParameterList( - SyntaxFactory.ParameterList( - SyntaxFactory.SeparatedList( + ParameterList( + SeparatedList( rawPfn .ParameterList.Parameters.SkipLast(1) .Select( (y, i) => - SyntaxFactory.Parameter( + Parameter( y.AttributeLists, y.Modifiers, y.Type, - SyntaxFactory.Identifier($"arg{i}"), + Identifier($"arg{i}"), null ) ) @@ -456,6 +395,9 @@ HashSet ReferencingNamespaces ) > FunctionPointerTypes { get; set; } = []; + public string? Namespace { get; set; } + public string? File { get; set; } + public override SyntaxNode? VisitIdentifierName(IdentifierNameSyntax node) => base.VisitIdentifierName( _typeRenames.TryGetValue(node.Identifier.ToString(), out var v) @@ -610,7 +552,7 @@ _fallbackFromOuterFunctionPointer is not null var ns = node.NamespaceFromSyntaxNode(); node = node.WithParameterList( node.ParameterList.WithParameters( - SyntaxFactory.SeparatedList( + SeparatedList( node.ParameterList.Parameters.Select( (x, i) => { @@ -638,26 +580,18 @@ _fallbackFromOuterFunctionPointer is not null $"{currentNativeTypeName}Delegate", ( currentNativeTypeName == fallback - ? SyntaxFactory.SingletonList( - SyntaxFactory.AttributeList( - SyntaxFactory.SingletonSeparatedList( - SyntaxFactory.Attribute( - SyntaxFactory.IdentifierName("Transformed") - ) - ) + ? SingletonList( + AttributeList( + SingletonSeparatedList(Attribute(IdentifierName("Transformed"))) ) ) : default ).WithNativeName(currentNativeTypeName), ( currentNativeTypeName == fallback - ? SyntaxFactory.SingletonList( - SyntaxFactory.AttributeList( - SyntaxFactory.SingletonSeparatedList( - SyntaxFactory.Attribute( - SyntaxFactory.IdentifierName("Transformed") - ) - ) + ? SingletonList( + AttributeList( + SingletonSeparatedList(Attribute(IdentifierName("Transformed"))) ) ) : default @@ -685,7 +619,7 @@ _fallbackFromOuterFunctionPointer is not null pfnInfo.ReferencingFileDirs.Add(dir); } - return SyntaxFactory.IdentifierName(currentNativeTypeName); + return IdentifierName(currentNativeTypeName); } [GeneratedRegex( diff --git a/sources/SilkTouch/SilkTouch/Mods/ExtractNestedTyping.cs b/sources/SilkTouch/SilkTouch/Mods/ExtractNestedTyping.cs index 3a3ffb90df..73523c060f 100644 --- a/sources/SilkTouch/SilkTouch/Mods/ExtractNestedTyping.cs +++ b/sources/SilkTouch/SilkTouch/Mods/ExtractNestedTyping.cs @@ -40,7 +40,7 @@ public override async Task ExecuteAsync(IModContext ctx, CancellationToken ct = continue; } - // Rewrite node + // Rewrite nodes // What this does depends on the node's type // // For example: From d569b7a7271571b258541e338daeb4dd829b1fb8 Mon Sep 17 00:00:00 2001 From: William Chen Date: Mon, 8 Jun 2026 06:19:54 -0400 Subject: [PATCH 07/37] Rename ExtractNestedTyping to ExtractNestedTypes This is be more consistent with C#'s terminology now that ExtractNestedTypes strictly only handles NestedTypes and not other forms of "nested typing". --- generator.json | 6 +++--- .../SilkTouch/SilkTouch/Mods/Common/ModLoader.cs | 2 +- .../SilkTouch/Mods/ExtractFunctionPointers.cs | 2 +- ...ractNestedTyping.cs => ExtractNestedTypes.cs} | 16 +++++----------- .../SilkTouch/SilkTouch/Mods/PrettifyNames.cs | 2 +- ...tractsCStyleEnumConstants_Field.verified.txt} | 0 ...leEnumConstants_MethodParameter.verified.txt} | 0 ...actsCStyleEnumConstants_Pointer.verified.txt} | 0 ...sCStyleEnumConstants_ReturnType.verified.txt} | 0 ...essfullyExtractsFunctionPointer.verified.txt} | 0 ...sfullyExtractsNestedInlineArray.verified.txt} | 0 ...TypingTests.cs => ExtractNestedTypesTests.cs} | 16 ++++++++-------- 12 files changed, 19 insertions(+), 25 deletions(-) rename sources/SilkTouch/SilkTouch/Mods/{ExtractNestedTyping.cs => ExtractNestedTypes.cs} (91%) rename tests/SilkTouch/SilkTouch/{ExtractNestedTypingTests.SuccessfullyExtractsCStyleEnumConstants_Field.verified.txt => ExtractNestedTypesTests.SuccessfullyExtractsCStyleEnumConstants_Field.verified.txt} (100%) rename tests/SilkTouch/SilkTouch/{ExtractNestedTypingTests.SuccessfullyExtractsCStyleEnumConstants_MethodParameter.verified.txt => ExtractNestedTypesTests.SuccessfullyExtractsCStyleEnumConstants_MethodParameter.verified.txt} (100%) rename tests/SilkTouch/SilkTouch/{ExtractNestedTypingTests.SuccessfullyExtractsCStyleEnumConstants_Pointer.verified.txt => ExtractNestedTypesTests.SuccessfullyExtractsCStyleEnumConstants_Pointer.verified.txt} (100%) rename tests/SilkTouch/SilkTouch/{ExtractNestedTypingTests.SuccessfullyExtractsCStyleEnumConstants_ReturnType.verified.txt => ExtractNestedTypesTests.SuccessfullyExtractsCStyleEnumConstants_ReturnType.verified.txt} (100%) rename tests/SilkTouch/SilkTouch/{ExtractNestedTypingTests.SuccessfullyExtractsFunctionPointer.verified.txt => ExtractNestedTypesTests.SuccessfullyExtractsFunctionPointer.verified.txt} (100%) rename tests/SilkTouch/SilkTouch/{ExtractNestedTypingTests.SuccessfullyExtractsNestedInlineArray.verified.txt => ExtractNestedTypesTests.SuccessfullyExtractsNestedInlineArray.verified.txt} (100%) rename tests/SilkTouch/SilkTouch/{ExtractNestedTypingTests.cs => ExtractNestedTypesTests.cs} (95%) diff --git a/generator.json b/generator.json index 6c68161c18..e1303853a0 100644 --- a/generator.json +++ b/generator.json @@ -43,7 +43,7 @@ "ClangScraper", "MarkNativeNames", "ExtractHandles", - "ExtractNestedTyping", + "ExtractNestedTypes", "TransformHandles", "TransformFunctions", "TransformProperties", @@ -254,7 +254,7 @@ "AddApiProfiles", "MixKhronosData", "ExtractHandles", - "ExtractNestedTyping", + "ExtractNestedTypes", "TransformHandles", "InterceptNativeFunctions", "TransformFunctions", @@ -397,7 +397,7 @@ "ClangScraper", "MarkNativeNames", "ExtractHandles", - "ExtractNestedTyping", + "ExtractNestedTypes", "TransformHandles", "MixKhronosData", "AddApiProfiles", diff --git a/sources/SilkTouch/SilkTouch/Mods/Common/ModLoader.cs b/sources/SilkTouch/SilkTouch/Mods/Common/ModLoader.cs index 4ea293bd12..ded27503ea 100644 --- a/sources/SilkTouch/SilkTouch/Mods/Common/ModLoader.cs +++ b/sources/SilkTouch/SilkTouch/Mods/Common/ModLoader.cs @@ -27,7 +27,7 @@ public class ModLoader nameof(ExtractEnumConstants) => typeof(ExtractEnumConstants), nameof(ExtractFunctionPointers) => typeof(ExtractFunctionPointers), nameof(ExtractHandles) => typeof(ExtractHandles), - nameof(ExtractNestedTyping) => typeof(ExtractNestedTyping), + nameof(ExtractNestedTypes) => typeof(ExtractNestedTypes), nameof(IdentifySharedPrefixes) => typeof(IdentifySharedPrefixes), nameof(InterceptNativeFunctions) => typeof(InterceptNativeFunctions), nameof(MarkNativeNames) => typeof(MarkNativeNames), diff --git a/sources/SilkTouch/SilkTouch/Mods/ExtractFunctionPointers.cs b/sources/SilkTouch/SilkTouch/Mods/ExtractFunctionPointers.cs index e7cefc749e..a7bc3df385 100644 --- a/sources/SilkTouch/SilkTouch/Mods/ExtractFunctionPointers.cs +++ b/sources/SilkTouch/SilkTouch/Mods/ExtractFunctionPointers.cs @@ -16,7 +16,7 @@ namespace Silk.NET.SilkTouch.Mods; /// Replaces function pointers identified by their s /// with delegates and function pointer structs. /// -public partial class ExtractFunctionPointers(ILogger logger) : Mod +public partial class ExtractFunctionPointers(ILogger logger) : Mod { /// public override async Task ExecuteAsync(IModContext ctx, CancellationToken ct = default) diff --git a/sources/SilkTouch/SilkTouch/Mods/ExtractNestedTyping.cs b/sources/SilkTouch/SilkTouch/Mods/ExtractNestedTypes.cs similarity index 91% rename from sources/SilkTouch/SilkTouch/Mods/ExtractNestedTyping.cs rename to sources/SilkTouch/SilkTouch/Mods/ExtractNestedTypes.cs index 73523c060f..e8a6832f70 100644 --- a/sources/SilkTouch/SilkTouch/Mods/ExtractNestedTyping.cs +++ b/sources/SilkTouch/SilkTouch/Mods/ExtractNestedTypes.cs @@ -15,7 +15,7 @@ namespace Silk.NET.SilkTouch.Mods; /// Extracts nested types into their own separate types. /// In particular, this also handles fixed buffers and anonymous structures output by . /// -public partial class ExtractNestedTyping : Mod +public partial class ExtractNestedTypes : Mod { /// public override async Task ExecuteAsync(IModContext ctx, CancellationToken ct = default) @@ -34,19 +34,13 @@ public override async Task ExecuteAsync(IModContext ctx, CancellationToken ct = var doc = project.GetDocument(docId) ?? throw new InvalidOperationException("Document missing"); - var (fname, node) = (doc.RelativePath(), await doc.GetSyntaxRootAsync(ct)); - if (fname is null) + var (file, node) = (doc.RelativePath(), await doc.GetSyntaxRootAsync(ct)); + if (file is null) { continue; } - // Rewrite nodes - // What this does depends on the node's type - // - // For example: - // This will handle removing nested structs. - // This is also where extracted enums are processed. - rewriter.File = fname; + rewriter.File = file; project = doc.WithSyntaxRoot( rewriter.Visit(node) ?? throw new InvalidOperationException("Rewriter returned null") @@ -74,7 +68,7 @@ rewriter.Namespace is not null : SingletonList(newStruct) ), filePath: project.FullPath( - $"{fname.AsSpan()[..fname.LastIndexOf('/')]}/{newStruct.Identifier}.gen.cs" + $"{file.AsSpan()[..file.LastIndexOf('/')]}/{newStruct.Identifier}.gen.cs" ) ) .Project; diff --git a/sources/SilkTouch/SilkTouch/Mods/PrettifyNames.cs b/sources/SilkTouch/SilkTouch/Mods/PrettifyNames.cs index afccdb95ab..131311c7bb 100644 --- a/sources/SilkTouch/SilkTouch/Mods/PrettifyNames.cs +++ b/sources/SilkTouch/SilkTouch/Mods/PrettifyNames.cs @@ -15,7 +15,7 @@ namespace Silk.NET.SilkTouch.Mods; /// A mod that will convert other naming conventions to the PascalCase nomenclature typically used in C#. /// /// -/// Does not support nested types. Please use before this mod. +/// Does not support nested types. Please use before this mod. /// Note that despite this, some initial work has been done to add nested type support so that it can be added when necessary. /// [ModConfiguration] diff --git a/tests/SilkTouch/SilkTouch/ExtractNestedTypingTests.SuccessfullyExtractsCStyleEnumConstants_Field.verified.txt b/tests/SilkTouch/SilkTouch/ExtractNestedTypesTests.SuccessfullyExtractsCStyleEnumConstants_Field.verified.txt similarity index 100% rename from tests/SilkTouch/SilkTouch/ExtractNestedTypingTests.SuccessfullyExtractsCStyleEnumConstants_Field.verified.txt rename to tests/SilkTouch/SilkTouch/ExtractNestedTypesTests.SuccessfullyExtractsCStyleEnumConstants_Field.verified.txt diff --git a/tests/SilkTouch/SilkTouch/ExtractNestedTypingTests.SuccessfullyExtractsCStyleEnumConstants_MethodParameter.verified.txt b/tests/SilkTouch/SilkTouch/ExtractNestedTypesTests.SuccessfullyExtractsCStyleEnumConstants_MethodParameter.verified.txt similarity index 100% rename from tests/SilkTouch/SilkTouch/ExtractNestedTypingTests.SuccessfullyExtractsCStyleEnumConstants_MethodParameter.verified.txt rename to tests/SilkTouch/SilkTouch/ExtractNestedTypesTests.SuccessfullyExtractsCStyleEnumConstants_MethodParameter.verified.txt diff --git a/tests/SilkTouch/SilkTouch/ExtractNestedTypingTests.SuccessfullyExtractsCStyleEnumConstants_Pointer.verified.txt b/tests/SilkTouch/SilkTouch/ExtractNestedTypesTests.SuccessfullyExtractsCStyleEnumConstants_Pointer.verified.txt similarity index 100% rename from tests/SilkTouch/SilkTouch/ExtractNestedTypingTests.SuccessfullyExtractsCStyleEnumConstants_Pointer.verified.txt rename to tests/SilkTouch/SilkTouch/ExtractNestedTypesTests.SuccessfullyExtractsCStyleEnumConstants_Pointer.verified.txt diff --git a/tests/SilkTouch/SilkTouch/ExtractNestedTypingTests.SuccessfullyExtractsCStyleEnumConstants_ReturnType.verified.txt b/tests/SilkTouch/SilkTouch/ExtractNestedTypesTests.SuccessfullyExtractsCStyleEnumConstants_ReturnType.verified.txt similarity index 100% rename from tests/SilkTouch/SilkTouch/ExtractNestedTypingTests.SuccessfullyExtractsCStyleEnumConstants_ReturnType.verified.txt rename to tests/SilkTouch/SilkTouch/ExtractNestedTypesTests.SuccessfullyExtractsCStyleEnumConstants_ReturnType.verified.txt diff --git a/tests/SilkTouch/SilkTouch/ExtractNestedTypingTests.SuccessfullyExtractsFunctionPointer.verified.txt b/tests/SilkTouch/SilkTouch/ExtractNestedTypesTests.SuccessfullyExtractsFunctionPointer.verified.txt similarity index 100% rename from tests/SilkTouch/SilkTouch/ExtractNestedTypingTests.SuccessfullyExtractsFunctionPointer.verified.txt rename to tests/SilkTouch/SilkTouch/ExtractNestedTypesTests.SuccessfullyExtractsFunctionPointer.verified.txt diff --git a/tests/SilkTouch/SilkTouch/ExtractNestedTypingTests.SuccessfullyExtractsNestedInlineArray.verified.txt b/tests/SilkTouch/SilkTouch/ExtractNestedTypesTests.SuccessfullyExtractsNestedInlineArray.verified.txt similarity index 100% rename from tests/SilkTouch/SilkTouch/ExtractNestedTypingTests.SuccessfullyExtractsNestedInlineArray.verified.txt rename to tests/SilkTouch/SilkTouch/ExtractNestedTypesTests.SuccessfullyExtractsNestedInlineArray.verified.txt diff --git a/tests/SilkTouch/SilkTouch/ExtractNestedTypingTests.cs b/tests/SilkTouch/SilkTouch/ExtractNestedTypesTests.cs similarity index 95% rename from tests/SilkTouch/SilkTouch/ExtractNestedTypingTests.cs rename to tests/SilkTouch/SilkTouch/ExtractNestedTypesTests.cs index d6e4d373d6..c14f0ba29a 100644 --- a/tests/SilkTouch/SilkTouch/ExtractNestedTypingTests.cs +++ b/tests/SilkTouch/SilkTouch/ExtractNestedTypesTests.cs @@ -6,9 +6,9 @@ namespace Silk.NET.SilkTouch.UnitTests; -public class ExtractNestedTypingTests +public class ExtractNestedTypesTests { - static ExtractNestedTypingTests() + static ExtractNestedTypesTests() { if (!VerifyDiffPlex.Initialized) { @@ -46,7 +46,7 @@ public struct _name_e__FixedBuffer var context = new DummyModContext() { SourceProject = project }; - var extractNestedTyping = new ExtractNestedTyping(NullLogger.Instance); + var extractNestedTyping = new ExtractNestedTypes(NullLogger.Instance); await extractNestedTyping.ExecuteAsync(context); @@ -85,7 +85,7 @@ public delegate* unmanaged< var context = new DummyModContext() { SourceProject = project }; - var extractNestedTyping = new ExtractNestedTyping(NullLogger.Instance); + var extractNestedTyping = new ExtractNestedTypes(NullLogger.Instance); await extractNestedTyping.ExecuteAsync(context); @@ -142,7 +142,7 @@ public class Test var context = new DummyModContext() { SourceProject = project }; - var extractNestedTyping = new ExtractNestedTyping(NullLogger.Instance); + var extractNestedTyping = new ExtractNestedTypes(NullLogger.Instance); await extractNestedTyping.ExecuteAsync(context); @@ -200,7 +200,7 @@ public static extern byte SDL_SetSurfaceBlendMode( var context = new DummyModContext() { SourceProject = project }; - var extractNestedTyping = new ExtractNestedTyping(NullLogger.Instance); + var extractNestedTyping = new ExtractNestedTypes(NullLogger.Instance); await extractNestedTyping.ExecuteAsync(context); @@ -258,7 +258,7 @@ public static extern byte SDL_GetSurfaceBlendMode( var context = new DummyModContext() { SourceProject = project }; - var extractNestedTyping = new ExtractNestedTyping(NullLogger.Instance); + var extractNestedTyping = new ExtractNestedTypes(NullLogger.Instance); await extractNestedTyping.ExecuteAsync(context); @@ -320,7 +320,7 @@ SDL_BlendOperation alphaOperation var context = new DummyModContext() { SourceProject = project }; - var extractNestedTyping = new ExtractNestedTyping(NullLogger.Instance); + var extractNestedTyping = new ExtractNestedTypes(NullLogger.Instance); await extractNestedTyping.ExecuteAsync(context); From 88b225ebb2fe914a33b82f7811b8a0f5bcd1932f Mon Sep 17 00:00:00 2001 From: William Chen Date: Mon, 8 Jun 2026 06:30:00 -0400 Subject: [PATCH 08/37] Split ExtractNestedTypes tests into 3 sets of tests and ensure project is compilable (if not correct) This is so I can start testing for expected behavior. --- .../SilkTouch/Mods/ExtractEnumConstants.cs | 76 ++--- .../SilkTouch/Mods/ExtractFunctionPointers.cs | 100 +++---- .../SilkTouch/ExtractEnumConstants.cs | 253 ++++++++++++++++ .../SilkTouch/ExtractFunctionPointersTests.cs | 60 ++++ .../SilkTouch/ExtractNestedTypesTests.cs | 271 +----------------- 5 files changed, 416 insertions(+), 344 deletions(-) create mode 100644 tests/SilkTouch/SilkTouch/ExtractEnumConstants.cs create mode 100644 tests/SilkTouch/SilkTouch/ExtractFunctionPointersTests.cs diff --git a/sources/SilkTouch/SilkTouch/Mods/ExtractEnumConstants.cs b/sources/SilkTouch/SilkTouch/Mods/ExtractEnumConstants.cs index 3ea337ab4c..503b97fba0 100644 --- a/sources/SilkTouch/SilkTouch/Mods/ExtractEnumConstants.cs +++ b/sources/SilkTouch/SilkTouch/Mods/ExtractEnumConstants.cs @@ -74,43 +74,43 @@ HashSet ReferencingNamespaces public override SyntaxNode? VisitPredefinedType(PredefinedTypeSyntax node) { - var nativeTypeName = GetNativeTypeNameForPredefinedType(node).ToString(); - if (nativeTypeName.Length > 0) - { - // Detect type discrepancies. - var thisType = node.Keyword.Kind(); - if (!_numericTypeNames.TryGetValue(nativeTypeName, out var numericTypeName)) - { - _numericTypeNames[nativeTypeName] = numericTypeName = (thisType, [], []); - } - - if ( - thisType - is not ( - SyntaxKind.ByteKeyword - or SyntaxKind.SByteKeyword - or SyntaxKind.ShortKeyword - or SyntaxKind.UShortKeyword - or SyntaxKind.IntKeyword - or SyntaxKind.UIntKeyword - or SyntaxKind.LongKeyword - or SyntaxKind.ULongKeyword - ) - || thisType != numericTypeName?.Type - ) - { - _numericTypeNames[nativeTypeName] = numericTypeName = null; - } - - if (numericTypeName is { } theTypeDetails) - { - theTypeDetails.ReferencingNamespaces.Add(node.NamespaceFromSyntaxNode()); - if (File?[..File.LastIndexOf('/')] is { } dir) - { - theTypeDetails.ReferencingFileDirs.Add(dir); - } - } - } + // var nativeTypeName = GetNativeTypeNameForPredefinedType(node).ToString(); + // if (nativeTypeName.Length > 0) + // { + // // Detect type discrepancies. + // var thisType = node.Keyword.Kind(); + // if (!_numericTypeNames.TryGetValue(nativeTypeName, out var numericTypeName)) + // { + // _numericTypeNames[nativeTypeName] = numericTypeName = (thisType, [], []); + // } + // + // if ( + // thisType + // is not ( + // SyntaxKind.ByteKeyword + // or SyntaxKind.SByteKeyword + // or SyntaxKind.ShortKeyword + // or SyntaxKind.UShortKeyword + // or SyntaxKind.IntKeyword + // or SyntaxKind.UIntKeyword + // or SyntaxKind.LongKeyword + // or SyntaxKind.ULongKeyword + // ) + // || thisType != numericTypeName?.Type + // ) + // { + // _numericTypeNames[nativeTypeName] = numericTypeName = null; + // } + // + // if (numericTypeName is { } theTypeDetails) + // { + // theTypeDetails.ReferencingNamespaces.Add(node.NamespaceFromSyntaxNode()); + // if (File?[..File.LastIndexOf('/')] is { } dir) + // { + // theTypeDetails.ReferencingFileDirs.Add(dir); + // } + // } + // } return base.VisitPredefinedType(node); } @@ -264,7 +264,7 @@ var constant in (IEnumerable) public override SyntaxNode? VisitEnumDeclaration(EnumDeclarationSyntax node) { - InvalidateIfSeen(_numericTypeNames, node.Identifier.ToString()); + // InvalidateIfSeen(_numericTypeNames, node.Identifier.ToString()); return base.VisitEnumDeclaration(node); } } diff --git a/sources/SilkTouch/SilkTouch/Mods/ExtractFunctionPointers.cs b/sources/SilkTouch/SilkTouch/Mods/ExtractFunctionPointers.cs index a7bc3df385..821ed1fbd5 100644 --- a/sources/SilkTouch/SilkTouch/Mods/ExtractFunctionPointers.cs +++ b/sources/SilkTouch/SilkTouch/Mods/ExtractFunctionPointers.cs @@ -66,16 +66,16 @@ public override async Task ExecuteAsync(IModContext ctx, CancellationToken ct = ), ] ) - .Concat( // TODO: Looks like I misnamed the variable when I refactored this last year. This handles both enums and function pointers - enums.Select(x => - ( - (MemberDeclarationSyntax)x.Value.Item1, - x.Value.Item1.Identifier.ToString(), - x.Value.Item2, - x.Value.Item3 - ) - ) - ) + // .Concat( // TODO: Looks like I misnamed the variable when I refactored this last year. This handles both enums and function pointers + // enums.Select(x => + // ( + // (MemberDeclarationSyntax)x.Value.Item1, + // x.Value.Item1.Identifier.ToString(), + // x.Value.Item2, + // x.Value.Item3 + // ) + // ) + // ) .ToList(); foreach (var (typeDecl, identifier, fileDirs, namespaces) in extractedFunctionPointers) @@ -415,11 +415,11 @@ is not null public override SyntaxNode? VisitPredefinedType(PredefinedTypeSyntax node) { - var nativeTypeName = GetNativeTypeNameForPredefinedType(node).ToString(); - if (ExtractedEnums?.Contains(nativeTypeName) ?? false) - { - return IdentifierName(nativeTypeName).WithTriviaFrom(node); - } + // var nativeTypeName = GetNativeTypeNameForPredefinedType(node).ToString(); + // if (ExtractedEnums?.Contains(nativeTypeName) ?? false) + // { + // return IdentifierName(nativeTypeName).WithTriviaFrom(node); + // } return base.VisitPredefinedType(node); } @@ -575,41 +575,41 @@ _fallbackFromOuterFunctionPointer is not null // Generate the types if we haven't already. if (!FunctionPointerTypes.TryGetValue(currentNativeTypeName, out var pfnInfo)) { - var (pfn, @delegate) = CreateFunctionPointerTypes( - currentNativeTypeName, - $"{currentNativeTypeName}Delegate", - ( - currentNativeTypeName == fallback - ? SingletonList( - AttributeList( - SingletonSeparatedList(Attribute(IdentifierName("Transformed"))) - ) - ) - : default - ).WithNativeName(currentNativeTypeName), - ( - currentNativeTypeName == fallback - ? SingletonList( - AttributeList( - SingletonSeparatedList(Attribute(IdentifierName("Transformed"))) - ) - ) - : default - ) - .WithNativeName(currentNativeTypeName) - .AddReferencedNameAffix( - NameAffixType.Prefix, - "FunctionPointerParent", - currentNativeTypeName - ) - .AddNameAffix( - NameAffixType.Suffix, - "FunctionPointerDelegateType", - "Delegate" - ), - node - ); - FunctionPointerTypes[currentNativeTypeName] = pfnInfo = (pfn, @delegate, [], []); + // var (pfn, @delegate) = CreateFunctionPointerTypes( + // currentNativeTypeName, + // $"{currentNativeTypeName}Delegate", + // ( + // currentNativeTypeName == fallback + // ? SingletonList( + // AttributeList( + // SingletonSeparatedList(Attribute(IdentifierName("Transformed"))) + // ) + // ) + // : default + // ).WithNativeName(currentNativeTypeName), + // ( + // currentNativeTypeName == fallback + // ? SingletonList( + // AttributeList( + // SingletonSeparatedList(Attribute(IdentifierName("Transformed"))) + // ) + // ) + // : default + // ) + // .WithNativeName(currentNativeTypeName) + // .AddReferencedNameAffix( + // NameAffixType.Prefix, + // "FunctionPointerParent", + // currentNativeTypeName + // ) + // .AddNameAffix( + // NameAffixType.Suffix, + // "FunctionPointerDelegateType", + // "Delegate" + // ), + // node + // ); + // FunctionPointerTypes[currentNativeTypeName] = pfnInfo = (pfn, @delegate, [], []); } // Ensure this visitation is used to determine the namespace/location. diff --git a/tests/SilkTouch/SilkTouch/ExtractEnumConstants.cs b/tests/SilkTouch/SilkTouch/ExtractEnumConstants.cs new file mode 100644 index 0000000000..6766327d33 --- /dev/null +++ b/tests/SilkTouch/SilkTouch/ExtractEnumConstants.cs @@ -0,0 +1,253 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Silk.NET.SilkTouch.Mods; + +namespace Silk.NET.SilkTouch.UnitTests; + +// TODO +public class ExtractEnumConstantsTests +{ + static ExtractEnumConstantsTests() + { + if (!VerifyDiffPlex.Initialized) + { + VerifyDiffPlex.Initialize(); + } + } + + [Test] + public async Task SuccessfullyExtractsCStyleEnumConstants_Field() + { + var inputDocName = "Sdl.gen.cs"; + var project = TestUtils + .CreateTestProject() + .AddDocument( + inputDocName, + """ + public unsafe partial struct Sdl + { + [NativeTypeName("#define SDL_BLENDMODE_NONE 0x00000000u")] + public const uint SDL_BLENDMODE_NONE = 0x00000000U; + + [NativeTypeName("#define SDL_BLENDMODE_BLEND 0x00000001u")] + public const uint SDL_BLENDMODE_BLEND = 0x00000001U; + + [NativeTypeName("#define SDL_BLENDMODE_BLEND_PREMULTIPLIED 0x00000010u")] + public const uint SDL_BLENDMODE_BLEND_PREMULTIPLIED = 0x00000010U; + + [NativeTypeName("#define SDL_BLENDMODE_ADD 0x00000002u")] + public const uint SDL_BLENDMODE_ADD = 0x00000002U; + + [NativeTypeName("#define SDL_BLENDMODE_ADD_PREMULTIPLIED 0x00000020u")] + public const uint SDL_BLENDMODE_ADD_PREMULTIPLIED = 0x00000020U; + + [NativeTypeName("#define SDL_BLENDMODE_MOD 0x00000004u")] + public const uint SDL_BLENDMODE_MOD = 0x00000004U; + + [NativeTypeName("#define SDL_BLENDMODE_MUL 0x00000008u")] + public const uint SDL_BLENDMODE_MUL = 0x00000008U; + + [NativeTypeName("#define SDL_BLENDMODE_INVALID 0x7FFFFFFFu")] + public const uint SDL_BLENDMODE_INVALID = 0x7FFFFFFFU; + } + + public class Test + { + [NativeTypeName("SDL_BlendMode")] + public uint Blend; + } + """, + // TODO: ExtractEnumConstants requires the file path to be set and that the document is under a subfolder + filePath: $"SDL3/{inputDocName}" + ) + .Project; + + var context = new DummyModContext() { SourceProject = project }; + + var extractEnumConstants = new ExtractEnumConstants(); + + await extractEnumConstants.ExecuteAsync(context); + + // The constants should have been moved from the Sdl clas to the SDL_BlendMode enum + await TestUtils.VerifyDocumentsAsync(context.SourceProject.Documents); + } + + [Test] + public async Task SuccessfullyExtractsCStyleEnumConstants_MethodParameter() + { + var inputDocName = "Sdl.gen.cs"; + var project = TestUtils + .CreateTestProject() + .AddDocument( + inputDocName, + """ + public unsafe partial struct Sdl + { + [NativeTypeName("#define SDL_BLENDMODE_NONE 0x00000000u")] + public const uint SDL_BLENDMODE_NONE = 0x00000000U; + + [NativeTypeName("#define SDL_BLENDMODE_BLEND 0x00000001u")] + public const uint SDL_BLENDMODE_BLEND = 0x00000001U; + + [NativeTypeName("#define SDL_BLENDMODE_BLEND_PREMULTIPLIED 0x00000010u")] + public const uint SDL_BLENDMODE_BLEND_PREMULTIPLIED = 0x00000010U; + + [NativeTypeName("#define SDL_BLENDMODE_ADD 0x00000002u")] + public const uint SDL_BLENDMODE_ADD = 0x00000002U; + + [NativeTypeName("#define SDL_BLENDMODE_ADD_PREMULTIPLIED 0x00000020u")] + public const uint SDL_BLENDMODE_ADD_PREMULTIPLIED = 0x00000020U; + + [NativeTypeName("#define SDL_BLENDMODE_MOD 0x00000004u")] + public const uint SDL_BLENDMODE_MOD = 0x00000004U; + + [NativeTypeName("#define SDL_BLENDMODE_MUL 0x00000008u")] + public const uint SDL_BLENDMODE_MUL = 0x00000008U; + + [NativeTypeName("#define SDL_BLENDMODE_INVALID 0x7FFFFFFFu")] + public const uint SDL_BLENDMODE_INVALID = 0x7FFFFFFFU; + + [DllImport("SDL3", ExactSpelling = true)] + [return: NativeTypeName("bool")] + public static extern byte SDL_SetSurfaceBlendMode( + SDL_Surface* surface, + [NativeTypeName("SDL_BlendMode")] uint blendMode + ); + } + """, + // ExtractNestedTyping requires the file path to be set and that the document is under a subfolder + filePath: $"SDL3/{inputDocName}" + ) + .Project; + + var context = new DummyModContext() { SourceProject = project }; + + var extractNestedTyping = new ExtractEnumConstants(); + + await extractNestedTyping.ExecuteAsync(context); + + // The constants should have been moved from the Sdl clas to the SDL_BlendMode enum + await TestUtils.VerifyDocumentsAsync(context.SourceProject.Documents); + } + + [Test] + public async Task SuccessfullyExtractsCStyleEnumConstants_Pointer() + { + var inputDocName = "Sdl.gen.cs"; + var project = TestUtils + .CreateTestProject() + .AddDocument( + inputDocName, + """ + public unsafe partial struct Sdl + { + [NativeTypeName("#define SDL_BLENDMODE_NONE 0x00000000u")] + public const uint SDL_BLENDMODE_NONE = 0x00000000U; + + [NativeTypeName("#define SDL_BLENDMODE_BLEND 0x00000001u")] + public const uint SDL_BLENDMODE_BLEND = 0x00000001U; + + [NativeTypeName("#define SDL_BLENDMODE_BLEND_PREMULTIPLIED 0x00000010u")] + public const uint SDL_BLENDMODE_BLEND_PREMULTIPLIED = 0x00000010U; + + [NativeTypeName("#define SDL_BLENDMODE_ADD 0x00000002u")] + public const uint SDL_BLENDMODE_ADD = 0x00000002U; + + [NativeTypeName("#define SDL_BLENDMODE_ADD_PREMULTIPLIED 0x00000020u")] + public const uint SDL_BLENDMODE_ADD_PREMULTIPLIED = 0x00000020U; + + [NativeTypeName("#define SDL_BLENDMODE_MOD 0x00000004u")] + public const uint SDL_BLENDMODE_MOD = 0x00000004U; + + [NativeTypeName("#define SDL_BLENDMODE_MUL 0x00000008u")] + public const uint SDL_BLENDMODE_MUL = 0x00000008U; + + [NativeTypeName("#define SDL_BLENDMODE_INVALID 0x7FFFFFFFu")] + public const uint SDL_BLENDMODE_INVALID = 0x7FFFFFFFU; + + [DllImport("SDL3", ExactSpelling = true)] + [return: NativeTypeName("bool")] + public static extern byte SDL_GetSurfaceBlendMode( + SDL_Surface* surface, + [NativeTypeName("SDL_BlendMode *")] uint* blendMode + ); + } + """, + // ExtractNestedTyping requires the file path to be set and that the document is under a subfolder + filePath: $"SDL3/{inputDocName}" + ) + .Project; + + var context = new DummyModContext() { SourceProject = project }; + + var extractNestedTyping = new ExtractEnumConstants(); + + await extractNestedTyping.ExecuteAsync(context); + + // The constants should have been moved from the Sdl clas to the SDL_BlendMode enum + await TestUtils.VerifyDocumentsAsync(context.SourceProject.Documents); + } + + [Test] + public async Task SuccessfullyExtractsCStyleEnumConstants_ReturnType() + { + var inputDocName = "Sdl.gen.cs"; + var project = TestUtils + .CreateTestProject() + .AddDocument( + inputDocName, + """ + public unsafe partial struct Sdl + { + [NativeTypeName("#define SDL_BLENDMODE_NONE 0x00000000u")] + public const uint SDL_BLENDMODE_NONE = 0x00000000U; + + [NativeTypeName("#define SDL_BLENDMODE_BLEND 0x00000001u")] + public const uint SDL_BLENDMODE_BLEND = 0x00000001U; + + [NativeTypeName("#define SDL_BLENDMODE_BLEND_PREMULTIPLIED 0x00000010u")] + public const uint SDL_BLENDMODE_BLEND_PREMULTIPLIED = 0x00000010U; + + [NativeTypeName("#define SDL_BLENDMODE_ADD 0x00000002u")] + public const uint SDL_BLENDMODE_ADD = 0x00000002U; + + [NativeTypeName("#define SDL_BLENDMODE_ADD_PREMULTIPLIED 0x00000020u")] + public const uint SDL_BLENDMODE_ADD_PREMULTIPLIED = 0x00000020U; + + [NativeTypeName("#define SDL_BLENDMODE_MOD 0x00000004u")] + public const uint SDL_BLENDMODE_MOD = 0x00000004U; + + [NativeTypeName("#define SDL_BLENDMODE_MUL 0x00000008u")] + public const uint SDL_BLENDMODE_MUL = 0x00000008U; + + [NativeTypeName("#define SDL_BLENDMODE_INVALID 0x7FFFFFFFu")] + public const uint SDL_BLENDMODE_INVALID = 0x7FFFFFFFU; + + [DllImport("SDL3", ExactSpelling = true)] + [return: NativeTypeName("SDL_BlendMode")] + public static extern uint SDL_ComposeCustomBlendMode( + SDL_BlendFactor srcColorFactor, + SDL_BlendFactor dstColorFactor, + SDL_BlendOperation colorOperation, + SDL_BlendFactor srcAlphaFactor, + SDL_BlendFactor dstAlphaFactor, + SDL_BlendOperation alphaOperation + ); + } + """, + // ExtractNestedTyping requires the file path to be set and that the document is under a subfolder + filePath: $"SDL3/{inputDocName}" + ) + .Project; + + var context = new DummyModContext() { SourceProject = project }; + + var extractNestedTyping = new ExtractEnumConstants(); + + await extractNestedTyping.ExecuteAsync(context); + + // The constants should have been moved from the Sdl clas to the SDL_BlendMode enum + await TestUtils.VerifyDocumentsAsync(context.SourceProject.Documents); + } +} diff --git a/tests/SilkTouch/SilkTouch/ExtractFunctionPointersTests.cs b/tests/SilkTouch/SilkTouch/ExtractFunctionPointersTests.cs new file mode 100644 index 0000000000..6e22bf3126 --- /dev/null +++ b/tests/SilkTouch/SilkTouch/ExtractFunctionPointersTests.cs @@ -0,0 +1,60 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.Extensions.Logging.Abstractions; +using Silk.NET.SilkTouch.Mods; + +namespace Silk.NET.SilkTouch.UnitTests; + +// TODO +public class ExtractFunctionPointersTests +{ + static ExtractFunctionPointersTests() + { + if (!VerifyDiffPlex.Initialized) + { + VerifyDiffPlex.Initialize(); + } + } + + [Test] + public async Task SuccessfullyExtractsFunctionPointer() + { + var inputDocName = "VkDebugReportCallbackCreateInfoEXT.gen.cs"; + var project = TestUtils + .CreateTestProject() + .AddDocument( + inputDocName, + """ + public unsafe partial struct VkDebugReportCallbackCreateInfoEXT + { + [NativeTypeName("PFN_vkDebugReportCallbackEXT")] + public delegate* unmanaged< + uint, + VkDebugReportObjectTypeEXT, + ulong, + nuint, + int, + sbyte*, + sbyte*, + void*, + uint> pfnCallback; + } + """, + // TODO: ExtractFunctionPointers requires the file path to be set and that the document is under a subfolder + filePath: $"Vulkan/{inputDocName}" + ) + .Project; + + var context = new DummyModContext() { SourceProject = project }; + + var extractFunctionPointers = new ExtractFunctionPointers( + NullLogger.Instance + ); + + await extractFunctionPointers.ExecuteAsync(context); + + // The function pointer should be extracted as both a struct and a delegate + await TestUtils.VerifyDocumentsAsync(context.SourceProject.Documents); + } +} diff --git a/tests/SilkTouch/SilkTouch/ExtractNestedTypesTests.cs b/tests/SilkTouch/SilkTouch/ExtractNestedTypesTests.cs index c14f0ba29a..6317f03a56 100644 --- a/tests/SilkTouch/SilkTouch/ExtractNestedTypesTests.cs +++ b/tests/SilkTouch/SilkTouch/ExtractNestedTypesTests.cs @@ -1,7 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using Microsoft.Extensions.Logging.Abstractions; using Silk.NET.SilkTouch.Mods; namespace Silk.NET.SilkTouch.UnitTests; @@ -39,14 +38,14 @@ public struct _name_e__FixedBuffer } } """, - // ExtractNestedTyping requires the file path to be set and that the document is under a subfolder + // TODO: ExtractNestedTyping requires the file path to be set and that the document is under a subfolder filePath: $"Vulkan/{inputDocName}" ) .Project; var context = new DummyModContext() { SourceProject = project }; - var extractNestedTyping = new ExtractNestedTypes(NullLogger.Instance); + var extractNestedTyping = new ExtractNestedTypes(); await extractNestedTyping.ExecuteAsync(context); @@ -55,276 +54,36 @@ public struct _name_e__FixedBuffer } [Test] - public async Task SuccessfullyExtractsFunctionPointer() + public async Task SuccessfullyExtractsNestedStructs() { - var inputDocName = "VkDebugReportCallbackCreateInfoEXT.gen.cs"; + var inputDocName = "Test.cs"; var project = TestUtils .CreateTestProject() .AddDocument( inputDocName, """ - public unsafe partial struct VkDebugReportCallbackCreateInfoEXT + public struct A { - [NativeTypeName("PFN_vkDebugReportCallbackEXT")] - public delegate* unmanaged< - uint, - VkDebugReportObjectTypeEXT, - ulong, - nuint, - int, - sbyte*, - sbyte*, - void*, - uint> pfnCallback; - } - """, - // ExtractNestedTyping requires the file path to be set and that the document is under a subfolder - filePath: $"Vulkan/{inputDocName}" - ) - .Project; - - var context = new DummyModContext() { SourceProject = project }; - - var extractNestedTyping = new ExtractNestedTypes(NullLogger.Instance); - - await extractNestedTyping.ExecuteAsync(context); - - // The function pointer should be extracted as both a struct and a delegate - await TestUtils.VerifyDocumentsAsync(context.SourceProject.Documents); - } - - [Test] - public async Task SuccessfullyExtractsCStyleEnumConstants_Field() - { - var inputDocName = "Sdl.gen.cs"; - var project = TestUtils - .CreateTestProject() - .AddDocument( - inputDocName, - """ - public unsafe partial struct Sdl - { - [NativeTypeName("#define SDL_BLENDMODE_NONE 0x00000000u")] - public const uint SDL_BLENDMODE_NONE = 0x00000000U; - - [NativeTypeName("#define SDL_BLENDMODE_BLEND 0x00000001u")] - public const uint SDL_BLENDMODE_BLEND = 0x00000001U; - - [NativeTypeName("#define SDL_BLENDMODE_BLEND_PREMULTIPLIED 0x00000010u")] - public const uint SDL_BLENDMODE_BLEND_PREMULTIPLIED = 0x00000010U; - - [NativeTypeName("#define SDL_BLENDMODE_ADD 0x00000002u")] - public const uint SDL_BLENDMODE_ADD = 0x00000002U; - - [NativeTypeName("#define SDL_BLENDMODE_ADD_PREMULTIPLIED 0x00000020u")] - public const uint SDL_BLENDMODE_ADD_PREMULTIPLIED = 0x00000020U; - - [NativeTypeName("#define SDL_BLENDMODE_MOD 0x00000004u")] - public const uint SDL_BLENDMODE_MOD = 0x00000004U; - - [NativeTypeName("#define SDL_BLENDMODE_MUL 0x00000008u")] - public const uint SDL_BLENDMODE_MUL = 0x00000008U; - - [NativeTypeName("#define SDL_BLENDMODE_INVALID 0x7FFFFFFFu")] - public const uint SDL_BLENDMODE_INVALID = 0x7FFFFFFFU; - } - - public class Test - { - [NativeTypeName("SDL_BlendMode")] - public uint Blend; - } - """, - // ExtractNestedTyping requires the file path to be set and that the document is under a subfolder - filePath: $"SDL3/{inputDocName}" - ) - .Project; - - var context = new DummyModContext() { SourceProject = project }; - - var extractNestedTyping = new ExtractNestedTypes(NullLogger.Instance); - - await extractNestedTyping.ExecuteAsync(context); - - // The constants should have been moved from the Sdl clas to the SDL_BlendMode enum - await TestUtils.VerifyDocumentsAsync(context.SourceProject.Documents); - } - - [Test] - public async Task SuccessfullyExtractsCStyleEnumConstants_MethodParameter() - { - var inputDocName = "Sdl.gen.cs"; - var project = TestUtils - .CreateTestProject() - .AddDocument( - inputDocName, - """ - public unsafe partial struct Sdl - { - [NativeTypeName("#define SDL_BLENDMODE_NONE 0x00000000u")] - public const uint SDL_BLENDMODE_NONE = 0x00000000U; - - [NativeTypeName("#define SDL_BLENDMODE_BLEND 0x00000001u")] - public const uint SDL_BLENDMODE_BLEND = 0x00000001U; - - [NativeTypeName("#define SDL_BLENDMODE_BLEND_PREMULTIPLIED 0x00000010u")] - public const uint SDL_BLENDMODE_BLEND_PREMULTIPLIED = 0x00000010U; - - [NativeTypeName("#define SDL_BLENDMODE_ADD 0x00000002u")] - public const uint SDL_BLENDMODE_ADD = 0x00000002U; - - [NativeTypeName("#define SDL_BLENDMODE_ADD_PREMULTIPLIED 0x00000020u")] - public const uint SDL_BLENDMODE_ADD_PREMULTIPLIED = 0x00000020U; - - [NativeTypeName("#define SDL_BLENDMODE_MOD 0x00000004u")] - public const uint SDL_BLENDMODE_MOD = 0x00000004U; - - [NativeTypeName("#define SDL_BLENDMODE_MUL 0x00000008u")] - public const uint SDL_BLENDMODE_MUL = 0x00000008U; - - [NativeTypeName("#define SDL_BLENDMODE_INVALID 0x7FFFFFFFu")] - public const uint SDL_BLENDMODE_INVALID = 0x7FFFFFFFU; - - [DllImport("SDL3", ExactSpelling = true)] - [return: NativeTypeName("bool")] - public static extern byte SDL_SetSurfaceBlendMode( - SDL_Surface* surface, - [NativeTypeName("SDL_BlendMode")] uint blendMode - ); - } - """, - // ExtractNestedTyping requires the file path to be set and that the document is under a subfolder - filePath: $"SDL3/{inputDocName}" - ) - .Project; - - var context = new DummyModContext() { SourceProject = project }; - - var extractNestedTyping = new ExtractNestedTypes(NullLogger.Instance); - - await extractNestedTyping.ExecuteAsync(context); - - // The constants should have been moved from the Sdl clas to the SDL_BlendMode enum - await TestUtils.VerifyDocumentsAsync(context.SourceProject.Documents); - } - - [Test] - public async Task SuccessfullyExtractsCStyleEnumConstants_Pointer() - { - var inputDocName = "Sdl.gen.cs"; - var project = TestUtils - .CreateTestProject() - .AddDocument( - inputDocName, - """ - public unsafe partial struct Sdl - { - [NativeTypeName("#define SDL_BLENDMODE_NONE 0x00000000u")] - public const uint SDL_BLENDMODE_NONE = 0x00000000U; - - [NativeTypeName("#define SDL_BLENDMODE_BLEND 0x00000001u")] - public const uint SDL_BLENDMODE_BLEND = 0x00000001U; - - [NativeTypeName("#define SDL_BLENDMODE_BLEND_PREMULTIPLIED 0x00000010u")] - public const uint SDL_BLENDMODE_BLEND_PREMULTIPLIED = 0x00000010U; - - [NativeTypeName("#define SDL_BLENDMODE_ADD 0x00000002u")] - public const uint SDL_BLENDMODE_ADD = 0x00000002U; - - [NativeTypeName("#define SDL_BLENDMODE_ADD_PREMULTIPLIED 0x00000020u")] - public const uint SDL_BLENDMODE_ADD_PREMULTIPLIED = 0x00000020U; - - [NativeTypeName("#define SDL_BLENDMODE_MOD 0x00000004u")] - public const uint SDL_BLENDMODE_MOD = 0x00000004U; - - [NativeTypeName("#define SDL_BLENDMODE_MUL 0x00000008u")] - public const uint SDL_BLENDMODE_MUL = 0x00000008U; - - [NativeTypeName("#define SDL_BLENDMODE_INVALID 0x7FFFFFFFu")] - public const uint SDL_BLENDMODE_INVALID = 0x7FFFFFFFU; - - [DllImport("SDL3", ExactSpelling = true)] - [return: NativeTypeName("bool")] - public static extern byte SDL_GetSurfaceBlendMode( - SDL_Surface* surface, - [NativeTypeName("SDL_BlendMode *")] uint* blendMode - ); - } - """, - // ExtractNestedTyping requires the file path to be set and that the document is under a subfolder - filePath: $"SDL3/{inputDocName}" - ) - .Project; - - var context = new DummyModContext() { SourceProject = project }; - - var extractNestedTyping = new ExtractNestedTypes(NullLogger.Instance); - - await extractNestedTyping.ExecuteAsync(context); - - // The constants should have been moved from the Sdl clas to the SDL_BlendMode enum - await TestUtils.VerifyDocumentsAsync(context.SourceProject.Documents); - } - - [Test] - public async Task SuccessfullyExtractsCStyleEnumConstants_ReturnType() - { - var inputDocName = "Sdl.gen.cs"; - var project = TestUtils - .CreateTestProject() - .AddDocument( - inputDocName, - """ - public unsafe partial struct Sdl - { - [NativeTypeName("#define SDL_BLENDMODE_NONE 0x00000000u")] - public const uint SDL_BLENDMODE_NONE = 0x00000000U; - - [NativeTypeName("#define SDL_BLENDMODE_BLEND 0x00000001u")] - public const uint SDL_BLENDMODE_BLEND = 0x00000001U; - - [NativeTypeName("#define SDL_BLENDMODE_BLEND_PREMULTIPLIED 0x00000010u")] - public const uint SDL_BLENDMODE_BLEND_PREMULTIPLIED = 0x00000010U; - - [NativeTypeName("#define SDL_BLENDMODE_ADD 0x00000002u")] - public const uint SDL_BLENDMODE_ADD = 0x00000002U; - - [NativeTypeName("#define SDL_BLENDMODE_ADD_PREMULTIPLIED 0x00000020u")] - public const uint SDL_BLENDMODE_ADD_PREMULTIPLIED = 0x00000020U; - - [NativeTypeName("#define SDL_BLENDMODE_MOD 0x00000004u")] - public const uint SDL_BLENDMODE_MOD = 0x00000004U; - - [NativeTypeName("#define SDL_BLENDMODE_MUL 0x00000008u")] - public const uint SDL_BLENDMODE_MUL = 0x00000008U; - - [NativeTypeName("#define SDL_BLENDMODE_INVALID 0x7FFFFFFFu")] - public const uint SDL_BLENDMODE_INVALID = 0x7FFFFFFFU; - - [DllImport("SDL3", ExactSpelling = true)] - [return: NativeTypeName("SDL_BlendMode")] - public static extern uint SDL_ComposeCustomBlendMode( - SDL_BlendFactor srcColorFactor, - SDL_BlendFactor dstColorFactor, - SDL_BlendOperation colorOperation, - SDL_BlendFactor srcAlphaFactor, - SDL_BlendFactor dstAlphaFactor, - SDL_BlendOperation alphaOperation - ); + public struct B + { + public struct C + { + } + } } """, - // ExtractNestedTyping requires the file path to be set and that the document is under a subfolder - filePath: $"SDL3/{inputDocName}" + // TODO: ExtractNestedTyping requires the file path to be set and that the document is under a subfolder + filePath: $"Tests/{inputDocName}" ) .Project; var context = new DummyModContext() { SourceProject = project }; - var extractNestedTyping = new ExtractNestedTypes(NullLogger.Instance); + var extractNestedTyping = new ExtractNestedTypes(); await extractNestedTyping.ExecuteAsync(context); - // The constants should have been moved from the Sdl clas to the SDL_BlendMode enum + // There should be 3 structs in separate documents named A, AB, and ABC await TestUtils.VerifyDocumentsAsync(context.SourceProject.Documents); } } From afa87924d6aba924180196f3339ab9c1cbe1d0e7 Mon Sep 17 00:00:00 2001 From: William Chen Date: Mon, 8 Jun 2026 06:43:16 -0400 Subject: [PATCH 09/37] Get ExtractNestedTypes working --- .../SilkTouch/Mods/ExtractNestedTypes.cs | 34 +++++++++++------- .../SilkTouch/ExtractNestedTypesTests.cs | 36 +------------------ 2 files changed, 22 insertions(+), 48 deletions(-) diff --git a/sources/SilkTouch/SilkTouch/Mods/ExtractNestedTypes.cs b/sources/SilkTouch/SilkTouch/Mods/ExtractNestedTypes.cs index e8a6832f70..2d6d2d6fbe 100644 --- a/sources/SilkTouch/SilkTouch/Mods/ExtractNestedTypes.cs +++ b/sources/SilkTouch/SilkTouch/Mods/ExtractNestedTypes.cs @@ -12,8 +12,8 @@ namespace Silk.NET.SilkTouch.Mods; /// -/// Extracts nested types into their own separate types. -/// In particular, this also handles fixed buffers and anonymous structures output by . +/// Extracts fixed buffers and anonymous structs output by +/// into their own files as non-nested structs. /// public partial class ExtractNestedTypes : Mod { @@ -91,6 +91,14 @@ private partial class Rewriter : CSharpSyntaxRewriter public string? Namespace { get; set; } public string? File { get; set; } + public override SyntaxNode? VisitIdentifierName(IdentifierNameSyntax node) => + base.VisitIdentifierName( + _typeRenames.TryGetValue(node.Identifier.ToString(), out var newType) + || (newType = null) is not null + ? node.WithIdentifier(Identifier(newType)) + : node + ); + public override SyntaxNode? VisitStructDeclaration(StructDeclarationSyntax node) { // Extract nested structs @@ -101,32 +109,32 @@ private partial class Rewriter : CSharpSyntaxRewriter var members = node.Members; for (var i = 0; i < members.Count; i++) { - var mem = members[i]; + var member = members[i]; if ( - mem is not StructDeclarationSyntax struc - || GeneratedNestedTypeRegex().Match(struc.Identifier.ToString()) + member is not StructDeclarationSyntax structNode + || GeneratedNestedTypeRegex().Match(structNode.Identifier.ToString()) is not { Success: true, Groups.Count: 3 } match ) { continue; } - var iden = $"{node.Identifier}{match.Groups[1].Value}"; - _typeRenames[struc.Identifier.ToString()] = iden; - struc = + var extractedIdentifier = $"{node.Identifier}{match.Groups[1].Value}"; + _typeRenames[structNode.Identifier.ToString()] = extractedIdentifier; + structNode = VisitStructDeclaration( - struc - .WithIdentifier(Identifier(iden)) + structNode + .WithIdentifier(Identifier(extractedIdentifier)) .WithAttributeLists( - struc.AttributeLists.AddReferencedNameAffix( + structNode.AttributeLists.AddReferencedNameAffix( NameAffixType.Prefix, "NestedStructParent", node.Identifier.ToString() ) ) ) as StructDeclarationSyntax - ?? struc; - ExtractedNestedStructs.Add(struc); + ?? structNode; + ExtractedNestedStructs.Add(structNode); members = members.RemoveAt(i--); } diff --git a/tests/SilkTouch/SilkTouch/ExtractNestedTypesTests.cs b/tests/SilkTouch/SilkTouch/ExtractNestedTypesTests.cs index 6317f03a56..f8c0f8cab0 100644 --- a/tests/SilkTouch/SilkTouch/ExtractNestedTypesTests.cs +++ b/tests/SilkTouch/SilkTouch/ExtractNestedTypesTests.cs @@ -38,7 +38,7 @@ public struct _name_e__FixedBuffer } } """, - // TODO: ExtractNestedTyping requires the file path to be set and that the document is under a subfolder + // ExtractNestedTyping requires the file path to be set and that the document is under a subfolder filePath: $"Vulkan/{inputDocName}" ) .Project; @@ -52,38 +52,4 @@ public struct _name_e__FixedBuffer // The nested struct should be extracted and named as VkPerformanceCounterDescriptionARMname await TestUtils.VerifyDocumentsAsync(context.SourceProject.Documents); } - - [Test] - public async Task SuccessfullyExtractsNestedStructs() - { - var inputDocName = "Test.cs"; - var project = TestUtils - .CreateTestProject() - .AddDocument( - inputDocName, - """ - public struct A - { - public struct B - { - public struct C - { - } - } - } - """, - // TODO: ExtractNestedTyping requires the file path to be set and that the document is under a subfolder - filePath: $"Tests/{inputDocName}" - ) - .Project; - - var context = new DummyModContext() { SourceProject = project }; - - var extractNestedTyping = new ExtractNestedTypes(); - - await extractNestedTyping.ExecuteAsync(context); - - // There should be 3 structs in separate documents named A, AB, and ABC - await TestUtils.VerifyDocumentsAsync(context.SourceProject.Documents); - } } From a969aeb942babe201be94532a05a54fd536f228b Mon Sep 17 00:00:00 2001 From: William Chen Date: Mon, 8 Jun 2026 06:50:28 -0400 Subject: [PATCH 10/37] Update test snapshot names --- ...ts.SuccessfullyExtractsCStyleEnumConstants_Field.verified.txt} | 0 ...fullyExtractsCStyleEnumConstants_MethodParameter.verified.txt} | 0 ....SuccessfullyExtractsCStyleEnumConstants_Pointer.verified.txt} | 0 ...ccessfullyExtractsCStyleEnumConstants_ReturnType.verified.txt} | 0 ...ointersTests.SuccessfullyExtractsFunctionPointer.verified.txt} | 0 5 files changed, 0 insertions(+), 0 deletions(-) rename tests/SilkTouch/SilkTouch/{ExtractNestedTypesTests.SuccessfullyExtractsCStyleEnumConstants_Field.verified.txt => ExtractEnumConstantsTests.SuccessfullyExtractsCStyleEnumConstants_Field.verified.txt} (100%) rename tests/SilkTouch/SilkTouch/{ExtractNestedTypesTests.SuccessfullyExtractsCStyleEnumConstants_MethodParameter.verified.txt => ExtractEnumConstantsTests.SuccessfullyExtractsCStyleEnumConstants_MethodParameter.verified.txt} (100%) rename tests/SilkTouch/SilkTouch/{ExtractNestedTypesTests.SuccessfullyExtractsCStyleEnumConstants_Pointer.verified.txt => ExtractEnumConstantsTests.SuccessfullyExtractsCStyleEnumConstants_Pointer.verified.txt} (100%) rename tests/SilkTouch/SilkTouch/{ExtractNestedTypesTests.SuccessfullyExtractsCStyleEnumConstants_ReturnType.verified.txt => ExtractEnumConstantsTests.SuccessfullyExtractsCStyleEnumConstants_ReturnType.verified.txt} (100%) rename tests/SilkTouch/SilkTouch/{ExtractNestedTypesTests.SuccessfullyExtractsFunctionPointer.verified.txt => ExtractFunctionPointersTests.SuccessfullyExtractsFunctionPointer.verified.txt} (100%) diff --git a/tests/SilkTouch/SilkTouch/ExtractNestedTypesTests.SuccessfullyExtractsCStyleEnumConstants_Field.verified.txt b/tests/SilkTouch/SilkTouch/ExtractEnumConstantsTests.SuccessfullyExtractsCStyleEnumConstants_Field.verified.txt similarity index 100% rename from tests/SilkTouch/SilkTouch/ExtractNestedTypesTests.SuccessfullyExtractsCStyleEnumConstants_Field.verified.txt rename to tests/SilkTouch/SilkTouch/ExtractEnumConstantsTests.SuccessfullyExtractsCStyleEnumConstants_Field.verified.txt diff --git a/tests/SilkTouch/SilkTouch/ExtractNestedTypesTests.SuccessfullyExtractsCStyleEnumConstants_MethodParameter.verified.txt b/tests/SilkTouch/SilkTouch/ExtractEnumConstantsTests.SuccessfullyExtractsCStyleEnumConstants_MethodParameter.verified.txt similarity index 100% rename from tests/SilkTouch/SilkTouch/ExtractNestedTypesTests.SuccessfullyExtractsCStyleEnumConstants_MethodParameter.verified.txt rename to tests/SilkTouch/SilkTouch/ExtractEnumConstantsTests.SuccessfullyExtractsCStyleEnumConstants_MethodParameter.verified.txt diff --git a/tests/SilkTouch/SilkTouch/ExtractNestedTypesTests.SuccessfullyExtractsCStyleEnumConstants_Pointer.verified.txt b/tests/SilkTouch/SilkTouch/ExtractEnumConstantsTests.SuccessfullyExtractsCStyleEnumConstants_Pointer.verified.txt similarity index 100% rename from tests/SilkTouch/SilkTouch/ExtractNestedTypesTests.SuccessfullyExtractsCStyleEnumConstants_Pointer.verified.txt rename to tests/SilkTouch/SilkTouch/ExtractEnumConstantsTests.SuccessfullyExtractsCStyleEnumConstants_Pointer.verified.txt diff --git a/tests/SilkTouch/SilkTouch/ExtractNestedTypesTests.SuccessfullyExtractsCStyleEnumConstants_ReturnType.verified.txt b/tests/SilkTouch/SilkTouch/ExtractEnumConstantsTests.SuccessfullyExtractsCStyleEnumConstants_ReturnType.verified.txt similarity index 100% rename from tests/SilkTouch/SilkTouch/ExtractNestedTypesTests.SuccessfullyExtractsCStyleEnumConstants_ReturnType.verified.txt rename to tests/SilkTouch/SilkTouch/ExtractEnumConstantsTests.SuccessfullyExtractsCStyleEnumConstants_ReturnType.verified.txt diff --git a/tests/SilkTouch/SilkTouch/ExtractNestedTypesTests.SuccessfullyExtractsFunctionPointer.verified.txt b/tests/SilkTouch/SilkTouch/ExtractFunctionPointersTests.SuccessfullyExtractsFunctionPointer.verified.txt similarity index 100% rename from tests/SilkTouch/SilkTouch/ExtractNestedTypesTests.SuccessfullyExtractsFunctionPointer.verified.txt rename to tests/SilkTouch/SilkTouch/ExtractFunctionPointersTests.SuccessfullyExtractsFunctionPointer.verified.txt From 9f120c6278511878d14271e5e1405f907217935a Mon Sep 17 00:00:00 2001 From: William Chen Date: Mon, 8 Jun 2026 07:10:34 -0400 Subject: [PATCH 11/37] Get ExtractFunctionPointers working --- .../SilkTouch/Mods/ExtractFunctionPointers.cs | 532 +++++++++--------- .../SilkTouch/Mods/ExtractNestedTypes.cs | 9 +- .../SilkTouch/ExtractFunctionPointersTests.cs | 1 - 3 files changed, 259 insertions(+), 283 deletions(-) diff --git a/sources/SilkTouch/SilkTouch/Mods/ExtractFunctionPointers.cs b/sources/SilkTouch/SilkTouch/Mods/ExtractFunctionPointers.cs index 821ed1fbd5..db82b5b584 100644 --- a/sources/SilkTouch/SilkTouch/Mods/ExtractFunctionPointers.cs +++ b/sources/SilkTouch/SilkTouch/Mods/ExtractFunctionPointers.cs @@ -29,23 +29,21 @@ public override async Task ExecuteAsync(IModContext ctx, CancellationToken ct = return; } - // First pass to gather data, such as the types to extract and generate - var walker = new Walker(); - foreach (var doc in project.Documents) + // Scan and extract function pointers + var rewriter = new Rewriter(logger); + foreach (var docId in project.DocumentIds) { - var (fname, node) = (doc.RelativePath(), await doc.GetSyntaxRootAsync(ct)); - if (fname is null) - { - continue; - } - - walker.File = fname; - walker.Visit(node); + var doc = + project.GetDocument(docId) + ?? throw new InvalidOperationException("Document missing"); + project = doc.WithSyntaxRoot( + rewriter.Visit(await doc.GetSyntaxRootAsync(ct)) + ?? throw new InvalidOperationException("Visit returned null.") + ).Project; } // Add documents for each extracted function pointer // This is moved out of the foreach statement for better debuggability - var rewriter = new Rewriter(logger); var extractedFunctionPointers = rewriter .FunctionPointerTypes.Values // .Where(x => x.IsUnique) @@ -104,63 +102,231 @@ public override async Task ExecuteAsync(IModContext ctx, CancellationToken ct = ctx.SourceProject = project; } - private static ReadOnlySpan GetNativeTypeNameForPredefinedType( - PredefinedTypeSyntax node, - Dictionary, HashSet)?>? numericTypeNames = null - ) + private partial class Rewriter(ILogger logger) : CSharpSyntaxRewriter { - // Walk up to the parameter or method. We only allow primitive integer types right now. - var current = node.Parent; - var indirectionLevels = 0; - while (current is PointerTypeSyntax) - { - indirectionLevels++; - current = current.Parent; - } + private string? _typeNameFromOuterFunctionPointer; + private string? _fallbackFromOuterFunctionPointer; - var attrs = current switch - { - MethodDeclarationSyntax meth => meth.AttributeLists, - ParameterSyntax param => param.AttributeLists, - _ => default, - }; + public Dictionary< + string, + ( + StructDeclarationSyntax Pfn, + DelegateDeclarationSyntax Delegate, + HashSet ReferencingFileDirs, + HashSet ReferencingNamespaces + ) + > FunctionPointerTypes { get; set; } = []; - if (attrs.Count == 0) - { - return default; - } + public string? Namespace { get; set; } + public string? File { get; set; } - if (!attrs.TryParseNativeTypeName(out var info)) + public override SyntaxNode VisitIdentifierName(IdentifierNameSyntax node) { - return null; + if (!FunctionPointerTypes.TryGetValue(node.Identifier.ToString(), out var pfnInfo)) + { + return node; + } + + return node.WithIdentifier(Identifier(pfnInfo.Pfn.Identifier.ToString())); } - // Ensure that the indirection levels indicated by the type name is the same as we've encountered when walking - // up the type. If this isn't, this indicates that the native type name is a typedef to a pointer and shouldn't - // be something that is mapped into an enum. - if (info.IndirectionLevels == indirectionLevels) + public override SyntaxNode VisitFunctionPointerType(FunctionPointerTypeSyntax node) { - return info.Name; - } + // Walk up the type. We expect only pointers above us, but we could encounter a function pointer type in + // which case we just ignore all this as we should already have a _currentNativeTypeName. Anything else and + // we don't have enough context for a fallback. + var current = node.Parent; + var indirectionLevels = 0; + while (current is PointerTypeSyntax) + { + indirectionLevels++; + current = current.Parent; + } - InvalidateIfSeen(numericTypeNames, info.Name); - return null; - } + // As above, get the native type name if we can and also get a fallback name based on context. + var (currentNativeTypeName, fallback) = current switch + { + MethodDeclarationSyntax meth => ( + meth.AttributeLists.GetNativeTypeName(SyntaxKind.ReturnKeyword), + $"{meth.Identifier}_r" + ), + ParameterSyntax { Parent.Parent: MethodDeclarationSyntax meth } param => ( + param.AttributeLists.GetNativeTypeName(), + $"{meth.Identifier}_{param.Identifier}" + ), + VariableDeclarationSyntax + { + Parent: FieldDeclarationSyntax { Parent: BaseTypeDeclarationSyntax type } fld + } vardec => ( + fld.AttributeLists.GetNativeTypeName(), + $"{type.Identifier}_{vardec.Variables[0].Identifier}" + ), + _ => (null, null), + }; - private static void InvalidateIfSeen( - Dictionary, HashSet)?>? numericTypeNames, - string nativeTypeName - ) - { - if (numericTypeNames?.ContainsKey(nativeTypeName) ?? false) - { - numericTypeNames[nativeTypeName] = null; - } - } + // If the native type name is actually the function pointer signature (i.e. not through a typedef) then we + // should pass the native type name down when recursing. + fallback = _fallbackFromOuterFunctionPointer ?? fallback; + currentNativeTypeName = + (_typeNameFromOuterFunctionPointer ?? currentNativeTypeName)?.Trim() ?? fallback; + string[]? recursiveTypeNames = null; + if (currentNativeTypeName.AsSpan().ContainsAnyExcept(NameUtils.IdentifierChars)) + { + var match = FunctionPointerNativeTypeNameRegex().Match(currentNativeTypeName!); + if (match.Success) + { + currentNativeTypeName = fallback; - private class Walker : CSharpSyntaxRewriter - { - public string? File { get; set; } + // NOTE: We expect the groups to be as follows: + // 0 = everything + // 1 = return type + // 2 = indirection levels + 1 + // 3 = comma separated parameter types + recursiveTypeNames = new string[ + 1 + + (match.Groups[3].Value.Length > 0 ? 1 : 0) + + match.Groups[3].Value.AsSpan().Count(',') + ]; + if (match.Groups[2].Value.AsSpan().Count('*') != indirectionLevels + 1) + { + logger.LogWarning( + "Unable to deal with function pointer usage at {} - mismatch of indirection " + + "levels: {} for {}", + node.GetLocation().GetLineSpan(), + node, + currentNativeTypeName + ); + return node; + } + + recursiveTypeNames[^1] = match.Groups[1].Value; + var @params = match + .Groups[3] + .Value.Split( + ',', + StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries + ); + for (var i = 0; i < @params.Length; i++) + { + recursiveTypeNames[i] = @params[i]; + } + } + else + { + // Maybe it's a pointer type? + var idSpan = currentNativeTypeName.AsSpan(); + if (idSpan.StartsWith("const ")) + { + idSpan = idSpan["const ".Length..]; + } + + // If the indirection levels match (and the only other non-identifier characters are whitespace) + // then we can use the identifier as the native name. + idSpan = idSpan.Trim(); + var badStart = idSpan.IndexOfAnyExcept(NameUtils.IdentifierChars); + var bad = idSpan[badStart..]; + currentNativeTypeName = + badStart == -1 + || ( + bad.Count('*') == indirectionLevels + && bad.Count(' ') == bad.Length - indirectionLevels + ) + ? idSpan[..badStart].ToString() + : fallback; + } + } + + if (currentNativeTypeName is null) + { + logger.LogWarning( + "Unable to deal with function pointer usage at {} - terminated at {}: {}", + node.GetLocation().GetLineSpan(), + current?.GetType().Name ?? "null", + current + ); + return node; + } + + // Assert that our state is valid given the tests we've done above before recursing. + Debug.Assert( + _fallbackFromOuterFunctionPointer is not null + == node.Ancestors().OfType().Any() + ); + + // Ensure that we've recursively generated and fixed up any function pointers contained within this function + // pointer. + var ns = node.NamespaceFromSyntaxNode(); + node = node.WithParameterList( + node.ParameterList.WithParameters( + SeparatedList( + node.ParameterList.Parameters.Select( + (x, i) => + { + var typeNameBefore = _typeNameFromOuterFunctionPointer; + var fallbackBefore = _fallbackFromOuterFunctionPointer; + _typeNameFromOuterFunctionPointer = recursiveTypeNames?[i]; + _fallbackFromOuterFunctionPointer = + $"{currentNativeTypeName}_p{i}"; + var ret = base.Visit(x); + _typeNameFromOuterFunctionPointer = typeNameBefore; + _fallbackFromOuterFunctionPointer = fallbackBefore; + return ret; + } + ) + .OfType() + ) + ) + ); + + // Generate the types if we haven't already. + if (!FunctionPointerTypes.TryGetValue(currentNativeTypeName, out var pfnInfo)) + { + var (pfn, @delegate) = CreateFunctionPointerTypes( + currentNativeTypeName, + $"{currentNativeTypeName}Delegate", + ( + currentNativeTypeName == fallback + ? SingletonList( + AttributeList( + SingletonSeparatedList(Attribute(IdentifierName("Transformed"))) + ) + ) + : default + ).WithNativeName(currentNativeTypeName), + ( + currentNativeTypeName == fallback + ? SingletonList( + AttributeList( + SingletonSeparatedList(Attribute(IdentifierName("Transformed"))) + ) + ) + : default + ) + .WithNativeName(currentNativeTypeName) + .AddReferencedNameAffix( + NameAffixType.Prefix, + "FunctionPointerParent", + currentNativeTypeName + ) + .AddNameAffix( + NameAffixType.Suffix, + "FunctionPointerDelegateType", + "Delegate" + ), + node + ); + FunctionPointerTypes[currentNativeTypeName] = pfnInfo = (pfn, @delegate, [], []); + } + + // Ensure this visitation is used to determine the namespace/location. + pfnInfo.ReferencingNamespaces.Add(ns); + if (File?[..File.LastIndexOf('/')] is { } dir) + { + pfnInfo.ReferencingFileDirs.Add(dir); + } + + return IdentifierName(currentNativeTypeName); + } private static ( StructDeclarationSyntax Pfn, @@ -376,59 +542,14 @@ FunctionPointerTypeSyntax rawPfn ); return (pfn, @delegate); } - } - - private partial class Rewriter(ILogger logger) : CSharpSyntaxRewriter - { - private Dictionary _typeRenames = []; - - private string? _typeNameFromOuterFunctionPointer; - private string? _fallbackFromOuterFunctionPointer; - public Dictionary< - string, - ( - StructDeclarationSyntax Pfn, - DelegateDeclarationSyntax Delegate, - HashSet ReferencingFileDirs, - HashSet ReferencingNamespaces - ) - > FunctionPointerTypes { get; set; } = []; - - public string? Namespace { get; set; } - public string? File { get; set; } - - public override SyntaxNode? VisitIdentifierName(IdentifierNameSyntax node) => - base.VisitIdentifierName( - _typeRenames.TryGetValue(node.Identifier.ToString(), out var v) - || ( - v = - FunctionPointerTypes?.TryGetValue(node.Identifier.ToString(), out var pfni) - ?? false - ? pfni.Pfn.Identifier.ToString() - : null - ) - is not null - ? node.WithIdentifier(Identifier(v)) - : node - ); - - public override SyntaxNode? VisitPredefinedType(PredefinedTypeSyntax node) - { - // var nativeTypeName = GetNativeTypeNameForPredefinedType(node).ToString(); - // if (ExtractedEnums?.Contains(nativeTypeName) ?? false) - // { - // return IdentifierName(nativeTypeName).WithTriviaFrom(node); - // } - - return base.VisitPredefinedType(node); - } - - public override SyntaxNode? VisitFunctionPointerType(FunctionPointerTypeSyntax node) + private static ReadOnlySpan GetNativeTypeNameForPredefinedType( + PredefinedTypeSyntax node, + Dictionary, HashSet)?>? numericTypeNames = + null + ) { - // Walk up the type. We expect only pointers above us, but we could encounter a function pointer type in - // which case we just ignore all this as we should already have a _currentNativeTypeName. Anything else and - // we don't have enough context for a fallback. + // Walk up to the parameter or method. We only allow primitive integer types right now. var current = node.Parent; var indirectionLevels = 0; while (current is PointerTypeSyntax) @@ -437,189 +558,44 @@ is not null current = current.Parent; } - // As above, get the native type name if we can and also get a fallback name based on context. - var (currentNativeTypeName, fallback) = current switch + var attrs = current switch { - MethodDeclarationSyntax meth => ( - meth.AttributeLists.GetNativeTypeName(SyntaxKind.ReturnKeyword), - $"{meth.Identifier}_r" - ), - ParameterSyntax { Parent.Parent: MethodDeclarationSyntax meth } param => ( - param.AttributeLists.GetNativeTypeName(), - $"{meth.Identifier}_{param.Identifier}" - ), - VariableDeclarationSyntax - { - Parent: FieldDeclarationSyntax { Parent: BaseTypeDeclarationSyntax type } fld - } vardec => ( - fld.AttributeLists.GetNativeTypeName(), - $"{type.Identifier}_{vardec.Variables[0].Identifier}" - ), - _ => (null, null), + MethodDeclarationSyntax meth => meth.AttributeLists, + ParameterSyntax param => param.AttributeLists, + _ => default, }; - // If the native type name is actually the function pointer signature (i.e. not through a typedef) then we - // should pass the native type name down when recursing. - fallback = _fallbackFromOuterFunctionPointer ?? fallback; - currentNativeTypeName = - (_typeNameFromOuterFunctionPointer ?? currentNativeTypeName)?.Trim() ?? fallback; - string[]? recursiveTypeNames = null; - if (currentNativeTypeName.AsSpan().ContainsAnyExcept(NameUtils.IdentifierChars)) + if (attrs.Count == 0) { - var match = FunctionPointerNativeTypeNameRegex().Match(currentNativeTypeName!); - if (match.Success) - { - currentNativeTypeName = fallback; - - // NOTE: We expect the groups to be as follows: - // 0 = everything - // 1 = return type - // 2 = indirection levels + 1 - // 3 = comma separated parameter types - recursiveTypeNames = new string[ - 1 - + (match.Groups[3].Value.Length > 0 ? 1 : 0) - + match.Groups[3].Value.AsSpan().Count(',') - ]; - if (match.Groups[2].Value.AsSpan().Count('*') != indirectionLevels + 1) - { - logger.LogWarning( - "Unable to deal with function pointer usage at {} - mismatch of indirection " - + "levels: {} for {}", - node.GetLocation().GetLineSpan(), - node, - currentNativeTypeName - ); - return node; - } - - recursiveTypeNames[^1] = match.Groups[1].Value; - var @params = match - .Groups[3] - .Value.Split( - ',', - StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries - ); - for (var i = 0; i < @params.Length; i++) - { - recursiveTypeNames[i] = @params[i]; - } - } - else - { - // Maybe it's a pointer type? - var idSpan = currentNativeTypeName.AsSpan(); - if (idSpan.StartsWith("const ")) - { - idSpan = idSpan["const ".Length..]; - } - - // If the indirection levels match (and the only other non-identifier characters are whitespace) - // then we can use the identifier as the native name. - idSpan = idSpan.Trim(); - var badStart = idSpan.IndexOfAnyExcept(NameUtils.IdentifierChars); - var bad = idSpan[badStart..]; - currentNativeTypeName = - badStart == -1 - || ( - bad.Count('*') == indirectionLevels - && bad.Count(' ') == bad.Length - indirectionLevels - ) - ? idSpan[..badStart].ToString() - : fallback; - } + return default; } - if (currentNativeTypeName is null) + if (!attrs.TryParseNativeTypeName(out var info)) { - logger.LogWarning( - "Unable to deal with function pointer usage at {} - terminated at {}: {}", - node.GetLocation().GetLineSpan(), - current?.GetType().Name ?? "null", - current - ); - return node; + return null; } - // Assert that our state is valid given the tests we've done above before recursing. - Debug.Assert( - _fallbackFromOuterFunctionPointer is not null - == node.Ancestors().OfType().Any() - ); - - // Ensure that we've recursively generated and fixed up any function pointers contained within this function - // pointer. - var ns = node.NamespaceFromSyntaxNode(); - node = node.WithParameterList( - node.ParameterList.WithParameters( - SeparatedList( - node.ParameterList.Parameters.Select( - (x, i) => - { - var typeNameBefore = _typeNameFromOuterFunctionPointer; - var fallbackBefore = _fallbackFromOuterFunctionPointer; - _typeNameFromOuterFunctionPointer = recursiveTypeNames?[i]; - _fallbackFromOuterFunctionPointer = - $"{currentNativeTypeName}_p{i}"; - var ret = base.Visit(x); - _typeNameFromOuterFunctionPointer = typeNameBefore; - _fallbackFromOuterFunctionPointer = fallbackBefore; - return ret; - } - ) - .OfType() - ) - ) - ); - - // Generate the types if we haven't already. - if (!FunctionPointerTypes.TryGetValue(currentNativeTypeName, out var pfnInfo)) + // Ensure that the indirection levels indicated by the type name is the same as we've encountered when walking + // up the type. If this isn't, this indicates that the native type name is a typedef to a pointer and shouldn't + // be something that is mapped into an enum. + if (info.IndirectionLevels == indirectionLevels) { - // var (pfn, @delegate) = CreateFunctionPointerTypes( - // currentNativeTypeName, - // $"{currentNativeTypeName}Delegate", - // ( - // currentNativeTypeName == fallback - // ? SingletonList( - // AttributeList( - // SingletonSeparatedList(Attribute(IdentifierName("Transformed"))) - // ) - // ) - // : default - // ).WithNativeName(currentNativeTypeName), - // ( - // currentNativeTypeName == fallback - // ? SingletonList( - // AttributeList( - // SingletonSeparatedList(Attribute(IdentifierName("Transformed"))) - // ) - // ) - // : default - // ) - // .WithNativeName(currentNativeTypeName) - // .AddReferencedNameAffix( - // NameAffixType.Prefix, - // "FunctionPointerParent", - // currentNativeTypeName - // ) - // .AddNameAffix( - // NameAffixType.Suffix, - // "FunctionPointerDelegateType", - // "Delegate" - // ), - // node - // ); - // FunctionPointerTypes[currentNativeTypeName] = pfnInfo = (pfn, @delegate, [], []); + return info.Name; } - // Ensure this visitation is used to determine the namespace/location. - pfnInfo.ReferencingNamespaces.Add(ns); - if (File?[..File.LastIndexOf('/')] is { } dir) + InvalidateIfSeen(numericTypeNames, info.Name); + return null; + } + + private static void InvalidateIfSeen( + Dictionary, HashSet)?>? numericTypeNames, + string nativeTypeName + ) + { + if (numericTypeNames?.ContainsKey(nativeTypeName) ?? false) { - pfnInfo.ReferencingFileDirs.Add(dir); + numericTypeNames[nativeTypeName] = null; } - - return IdentifierName(currentNativeTypeName); } [GeneratedRegex( diff --git a/sources/SilkTouch/SilkTouch/Mods/ExtractNestedTypes.cs b/sources/SilkTouch/SilkTouch/Mods/ExtractNestedTypes.cs index 2d6d2d6fbe..7955cd76ff 100644 --- a/sources/SilkTouch/SilkTouch/Mods/ExtractNestedTypes.cs +++ b/sources/SilkTouch/SilkTouch/Mods/ExtractNestedTypes.cs @@ -28,22 +28,23 @@ public override async Task ExecuteAsync(IModContext ctx, CancellationToken ct = return; } + // Scan and extract nested structs var rewriter = new Rewriter(); foreach (var docId in project.DocumentIds) { var doc = project.GetDocument(docId) ?? throw new InvalidOperationException("Document missing"); - var (file, node) = (doc.RelativePath(), await doc.GetSyntaxRootAsync(ct)); + + var file = doc.RelativePath(); if (file is null) { continue; } - rewriter.File = file; project = doc.WithSyntaxRoot( - rewriter.Visit(node) - ?? throw new InvalidOperationException("Rewriter returned null") + rewriter.Visit(await doc.GetSyntaxRootAsync(ct)) + ?? throw new InvalidOperationException("Visit returned null.") ).Project; foreach (var newStruct in rewriter.ExtractedNestedStructs) diff --git a/tests/SilkTouch/SilkTouch/ExtractFunctionPointersTests.cs b/tests/SilkTouch/SilkTouch/ExtractFunctionPointersTests.cs index 6e22bf3126..4c86743485 100644 --- a/tests/SilkTouch/SilkTouch/ExtractFunctionPointersTests.cs +++ b/tests/SilkTouch/SilkTouch/ExtractFunctionPointersTests.cs @@ -6,7 +6,6 @@ namespace Silk.NET.SilkTouch.UnitTests; -// TODO public class ExtractFunctionPointersTests { static ExtractFunctionPointersTests() From bd48a6815eb8e23dfc2a36459a3b80af0f24b4dd Mon Sep 17 00:00:00 2001 From: William Chen Date: Mon, 8 Jun 2026 07:24:10 -0400 Subject: [PATCH 12/37] Fix subtly incorrect behavior and remove dependency on Mod for ExtractFunctionPointers/NestedTypes --- .../SilkTouch/Mods/ExtractFunctionPointers.cs | 28 ++++++++----------- .../SilkTouch/Mods/ExtractNestedTypes.cs | 7 ++--- .../SilkTouch/ExtractFunctionPointersTests.cs | 2 +- 3 files changed, 15 insertions(+), 22 deletions(-) diff --git a/sources/SilkTouch/SilkTouch/Mods/ExtractFunctionPointers.cs b/sources/SilkTouch/SilkTouch/Mods/ExtractFunctionPointers.cs index db82b5b584..0ec6584cae 100644 --- a/sources/SilkTouch/SilkTouch/Mods/ExtractFunctionPointers.cs +++ b/sources/SilkTouch/SilkTouch/Mods/ExtractFunctionPointers.cs @@ -16,13 +16,11 @@ namespace Silk.NET.SilkTouch.Mods; /// Replaces function pointers identified by their s /// with delegates and function pointer structs. /// -public partial class ExtractFunctionPointers(ILogger logger) : Mod +public partial class ExtractFunctionPointers(ILogger logger) : IMod { /// - public override async Task ExecuteAsync(IModContext ctx, CancellationToken ct = default) + public async Task ExecuteAsync(IModContext ctx, CancellationToken ct = default) { - await base.ExecuteAsync(ctx, ct); - var project = ctx.SourceProject; if (project == null) { @@ -36,6 +34,14 @@ public override async Task ExecuteAsync(IModContext ctx, CancellationToken ct = var doc = project.GetDocument(docId) ?? throw new InvalidOperationException("Document missing"); + + var file = doc.RelativePath(); + if (file is null) + { + continue; + } + + rewriter.File = file; project = doc.WithSyntaxRoot( rewriter.Visit(await doc.GetSyntaxRootAsync(ct)) ?? throw new InvalidOperationException("Visit returned null.") @@ -45,9 +51,7 @@ public override async Task ExecuteAsync(IModContext ctx, CancellationToken ct = // Add documents for each extracted function pointer // This is moved out of the foreach statement for better debuggability var extractedFunctionPointers = rewriter - .FunctionPointerTypes.Values - // .Where(x => x.IsUnique) - .SelectMany(x => + .FunctionPointerTypes.Values.SelectMany(x => (IEnumerable<(MemberDeclarationSyntax, string, HashSet, HashSet)>) [ ( @@ -64,16 +68,6 @@ public override async Task ExecuteAsync(IModContext ctx, CancellationToken ct = ), ] ) - // .Concat( // TODO: Looks like I misnamed the variable when I refactored this last year. This handles both enums and function pointers - // enums.Select(x => - // ( - // (MemberDeclarationSyntax)x.Value.Item1, - // x.Value.Item1.Identifier.ToString(), - // x.Value.Item2, - // x.Value.Item3 - // ) - // ) - // ) .ToList(); foreach (var (typeDecl, identifier, fileDirs, namespaces) in extractedFunctionPointers) diff --git a/sources/SilkTouch/SilkTouch/Mods/ExtractNestedTypes.cs b/sources/SilkTouch/SilkTouch/Mods/ExtractNestedTypes.cs index 7955cd76ff..6e9ec2ed6c 100644 --- a/sources/SilkTouch/SilkTouch/Mods/ExtractNestedTypes.cs +++ b/sources/SilkTouch/SilkTouch/Mods/ExtractNestedTypes.cs @@ -15,13 +15,11 @@ namespace Silk.NET.SilkTouch.Mods; /// Extracts fixed buffers and anonymous structs output by /// into their own files as non-nested structs. /// -public partial class ExtractNestedTypes : Mod +public partial class ExtractNestedTypes : IMod { /// - public override async Task ExecuteAsync(IModContext ctx, CancellationToken ct = default) + public async Task ExecuteAsync(IModContext ctx, CancellationToken ct = default) { - await base.ExecuteAsync(ctx, ct); - var project = ctx.SourceProject; if (project == null) { @@ -42,6 +40,7 @@ public override async Task ExecuteAsync(IModContext ctx, CancellationToken ct = continue; } + rewriter.File = file; project = doc.WithSyntaxRoot( rewriter.Visit(await doc.GetSyntaxRootAsync(ct)) ?? throw new InvalidOperationException("Visit returned null.") diff --git a/tests/SilkTouch/SilkTouch/ExtractFunctionPointersTests.cs b/tests/SilkTouch/SilkTouch/ExtractFunctionPointersTests.cs index 4c86743485..0f53b33375 100644 --- a/tests/SilkTouch/SilkTouch/ExtractFunctionPointersTests.cs +++ b/tests/SilkTouch/SilkTouch/ExtractFunctionPointersTests.cs @@ -40,7 +40,7 @@ public delegate* unmanaged< uint> pfnCallback; } """, - // TODO: ExtractFunctionPointers requires the file path to be set and that the document is under a subfolder + // ExtractFunctionPointers requires the file path to be set and that the document is under a subfolder filePath: $"Vulkan/{inputDocName}" ) .Project; From de0a8ca3f68e1fce9f5648cbb06b353603b8503f Mon Sep 17 00:00:00 2001 From: William Chen Date: Mon, 8 Jun 2026 07:28:44 -0400 Subject: [PATCH 13/37] Move GetNativeTypeNameForPredefinedType to ExtractEnumConstants and cleanup --- .../SilkTouch/Mods/ExtractEnumConstants.cs | 132 +++++++++++++----- .../SilkTouch/Mods/ExtractFunctionPointers.cs | 58 +------- .../SilkTouch/Mods/ExtractNestedTypes.cs | 3 - 3 files changed, 95 insertions(+), 98 deletions(-) diff --git a/sources/SilkTouch/SilkTouch/Mods/ExtractEnumConstants.cs b/sources/SilkTouch/SilkTouch/Mods/ExtractEnumConstants.cs index 503b97fba0..6813d46bb5 100644 --- a/sources/SilkTouch/SilkTouch/Mods/ExtractEnumConstants.cs +++ b/sources/SilkTouch/SilkTouch/Mods/ExtractEnumConstants.cs @@ -74,43 +74,44 @@ HashSet ReferencingNamespaces public override SyntaxNode? VisitPredefinedType(PredefinedTypeSyntax node) { - // var nativeTypeName = GetNativeTypeNameForPredefinedType(node).ToString(); - // if (nativeTypeName.Length > 0) - // { - // // Detect type discrepancies. - // var thisType = node.Keyword.Kind(); - // if (!_numericTypeNames.TryGetValue(nativeTypeName, out var numericTypeName)) - // { - // _numericTypeNames[nativeTypeName] = numericTypeName = (thisType, [], []); - // } - // - // if ( - // thisType - // is not ( - // SyntaxKind.ByteKeyword - // or SyntaxKind.SByteKeyword - // or SyntaxKind.ShortKeyword - // or SyntaxKind.UShortKeyword - // or SyntaxKind.IntKeyword - // or SyntaxKind.UIntKeyword - // or SyntaxKind.LongKeyword - // or SyntaxKind.ULongKeyword - // ) - // || thisType != numericTypeName?.Type - // ) - // { - // _numericTypeNames[nativeTypeName] = numericTypeName = null; - // } - // - // if (numericTypeName is { } theTypeDetails) - // { - // theTypeDetails.ReferencingNamespaces.Add(node.NamespaceFromSyntaxNode()); - // if (File?[..File.LastIndexOf('/')] is { } dir) - // { - // theTypeDetails.ReferencingFileDirs.Add(dir); - // } - // } - // } + var nativeTypeName = GetNativeTypeNameForPredefinedType(node).ToString(); + if (nativeTypeName.Length > 0) + { + // Detect type discrepancies. + var thisType = node.Keyword.Kind(); + if (!_numericTypeNames.TryGetValue(nativeTypeName, out var numericTypeName)) + { + _numericTypeNames[nativeTypeName] = numericTypeName = (thisType, [], []); + } + + if ( + thisType + is not ( + SyntaxKind.ByteKeyword + or SyntaxKind.SByteKeyword + or SyntaxKind.ShortKeyword + or SyntaxKind.UShortKeyword + or SyntaxKind.IntKeyword + or SyntaxKind.UIntKeyword + or SyntaxKind.LongKeyword + or SyntaxKind.ULongKeyword + ) + || thisType != numericTypeName?.Type + ) + { + _numericTypeNames[nativeTypeName] = numericTypeName = null; + } + + if (numericTypeName is { } theTypeDetails) + { + theTypeDetails.ReferencingNamespaces.Add(node.NamespaceFromSyntaxNode()); + if (File?[..File.LastIndexOf('/')] is { } dir) + { + theTypeDetails.ReferencingFileDirs.Add(dir); + } + } + } + return base.VisitPredefinedType(node); } @@ -264,9 +265,64 @@ var constant in (IEnumerable) public override SyntaxNode? VisitEnumDeclaration(EnumDeclarationSyntax node) { - // InvalidateIfSeen(_numericTypeNames, node.Identifier.ToString()); + InvalidateIfSeen(_numericTypeNames, node.Identifier.ToString()); return base.VisitEnumDeclaration(node); } + + private static ReadOnlySpan GetNativeTypeNameForPredefinedType( + PredefinedTypeSyntax node, + Dictionary, HashSet)?>? numericTypeNames = + null + ) + { + // Walk up to the parameter or method. We only allow primitive integer types right now. + var current = node.Parent; + var indirectionLevels = 0; + while (current is PointerTypeSyntax) + { + indirectionLevels++; + current = current.Parent; + } + + var attrs = current switch + { + MethodDeclarationSyntax meth => meth.AttributeLists, + ParameterSyntax param => param.AttributeLists, + _ => default, + }; + + if (attrs.Count == 0) + { + return default; + } + + if (!attrs.TryParseNativeTypeName(out var info)) + { + return null; + } + + // Ensure that the indirection levels indicated by the type name is the same as we've encountered when walking + // up the type. If this isn't, this indicates that the native type name is a typedef to a pointer and shouldn't + // be something that is mapped into an enum. + if (info.IndirectionLevels == indirectionLevels) + { + return info.Name; + } + + InvalidateIfSeen(numericTypeNames, info.Name); + return null; + } + + private static void InvalidateIfSeen( + Dictionary, HashSet)?>? numericTypeNames, + string nativeTypeName + ) + { + if (numericTypeNames?.ContainsKey(nativeTypeName) ?? false) + { + numericTypeNames[nativeTypeName] = null; + } + } } private class Rewriter : CSharpSyntaxRewriter diff --git a/sources/SilkTouch/SilkTouch/Mods/ExtractFunctionPointers.cs b/sources/SilkTouch/SilkTouch/Mods/ExtractFunctionPointers.cs index 0ec6584cae..805b464aec 100644 --- a/sources/SilkTouch/SilkTouch/Mods/ExtractFunctionPointers.cs +++ b/sources/SilkTouch/SilkTouch/Mods/ExtractFunctionPointers.cs @@ -109,9 +109,8 @@ public Dictionary< HashSet ReferencingFileDirs, HashSet ReferencingNamespaces ) - > FunctionPointerTypes { get; set; } = []; + > FunctionPointerTypes { get; } = []; - public string? Namespace { get; set; } public string? File { get; set; } public override SyntaxNode VisitIdentifierName(IdentifierNameSyntax node) @@ -537,61 +536,6 @@ FunctionPointerTypeSyntax rawPfn return (pfn, @delegate); } - private static ReadOnlySpan GetNativeTypeNameForPredefinedType( - PredefinedTypeSyntax node, - Dictionary, HashSet)?>? numericTypeNames = - null - ) - { - // Walk up to the parameter or method. We only allow primitive integer types right now. - var current = node.Parent; - var indirectionLevels = 0; - while (current is PointerTypeSyntax) - { - indirectionLevels++; - current = current.Parent; - } - - var attrs = current switch - { - MethodDeclarationSyntax meth => meth.AttributeLists, - ParameterSyntax param => param.AttributeLists, - _ => default, - }; - - if (attrs.Count == 0) - { - return default; - } - - if (!attrs.TryParseNativeTypeName(out var info)) - { - return null; - } - - // Ensure that the indirection levels indicated by the type name is the same as we've encountered when walking - // up the type. If this isn't, this indicates that the native type name is a typedef to a pointer and shouldn't - // be something that is mapped into an enum. - if (info.IndirectionLevels == indirectionLevels) - { - return info.Name; - } - - InvalidateIfSeen(numericTypeNames, info.Name); - return null; - } - - private static void InvalidateIfSeen( - Dictionary, HashSet)?>? numericTypeNames, - string nativeTypeName - ) - { - if (numericTypeNames?.ContainsKey(nativeTypeName) ?? false) - { - numericTypeNames[nativeTypeName] = null; - } - } - [GeneratedRegex( @"^((?:[A-Za-z0-9\s\*_]|\[[0-9]*\])+)\((\*)+\)\(((?:(?:[A-Za-z0-9\s\*_]|\[[0-9]*\])+,?)*)\)" )] diff --git a/sources/SilkTouch/SilkTouch/Mods/ExtractNestedTypes.cs b/sources/SilkTouch/SilkTouch/Mods/ExtractNestedTypes.cs index 6e9ec2ed6c..f1be8f91e0 100644 --- a/sources/SilkTouch/SilkTouch/Mods/ExtractNestedTypes.cs +++ b/sources/SilkTouch/SilkTouch/Mods/ExtractNestedTypes.cs @@ -40,7 +40,6 @@ public async Task ExecuteAsync(IModContext ctx, CancellationToken ct = default) continue; } - rewriter.File = file; project = doc.WithSyntaxRoot( rewriter.Visit(await doc.GetSyntaxRootAsync(ct)) ?? throw new InvalidOperationException("Visit returned null.") @@ -74,7 +73,6 @@ rewriter.Namespace is not null .Project; } - rewriter.File = null; rewriter.Namespace = null; rewriter.ExtractedNestedStructs.Clear(); } @@ -89,7 +87,6 @@ private partial class Rewriter : CSharpSyntaxRewriter public List ExtractedNestedStructs { get; } = []; public string? Namespace { get; set; } - public string? File { get; set; } public override SyntaxNode? VisitIdentifierName(IdentifierNameSyntax node) => base.VisitIdentifierName( From cd3a9f8de6633ac1678e3fdb6b064a134375cd95 Mon Sep 17 00:00:00 2001 From: William Chen Date: Mon, 8 Jun 2026 07:29:19 -0400 Subject: [PATCH 14/37] Fix incorrect file name --- .../{ExtractEnumConstants.cs => ExtractEnumConstantsTests.cs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/SilkTouch/SilkTouch/{ExtractEnumConstants.cs => ExtractEnumConstantsTests.cs} (100%) diff --git a/tests/SilkTouch/SilkTouch/ExtractEnumConstants.cs b/tests/SilkTouch/SilkTouch/ExtractEnumConstantsTests.cs similarity index 100% rename from tests/SilkTouch/SilkTouch/ExtractEnumConstants.cs rename to tests/SilkTouch/SilkTouch/ExtractEnumConstantsTests.cs From 06056fca0a425eaf7354e0223b3837fdcb5573bb Mon Sep 17 00:00:00 2001 From: William Chen Date: Mon, 8 Jun 2026 07:32:38 -0400 Subject: [PATCH 15/37] Prefer IMod over Mod where possible This also means free performance since Mod sneakily creates a compilation and walks through it --- sources/SilkTouch/SilkTouch/Mods/AddApiProfiles.cs | 5 ++--- sources/SilkTouch/SilkTouch/Mods/ExtractEnumConstants.cs | 6 ++---- sources/SilkTouch/SilkTouch/Mods/IdentifySharedPrefixes.cs | 4 ++-- sources/SilkTouch/SilkTouch/Mods/TransformHandles.cs | 6 ++---- 4 files changed, 8 insertions(+), 13 deletions(-) diff --git a/sources/SilkTouch/SilkTouch/Mods/AddApiProfiles.cs b/sources/SilkTouch/SilkTouch/Mods/AddApiProfiles.cs index 0df1ec07ea..5c2b818c55 100644 --- a/sources/SilkTouch/SilkTouch/Mods/AddApiProfiles.cs +++ b/sources/SilkTouch/SilkTouch/Mods/AddApiProfiles.cs @@ -28,7 +28,7 @@ public class AddApiProfiles( IEnumerable< IJobDependency>> > versionProviders -) : Mod +) : IMod { /// /// The mod configuration. @@ -209,9 +209,8 @@ out var parentSymbolBefore } /// - public override async Task ExecuteAsync(IModContext ctx, CancellationToken ct = default) + public async Task ExecuteAsync(IModContext ctx, CancellationToken ct = default) { - await base.ExecuteAsync(ctx, ct); if (ctx.SourceProject is null) { return; diff --git a/sources/SilkTouch/SilkTouch/Mods/ExtractEnumConstants.cs b/sources/SilkTouch/SilkTouch/Mods/ExtractEnumConstants.cs index 6813d46bb5..1d94daafa4 100644 --- a/sources/SilkTouch/SilkTouch/Mods/ExtractEnumConstants.cs +++ b/sources/SilkTouch/SilkTouch/Mods/ExtractEnumConstants.cs @@ -19,13 +19,11 @@ namespace Silk.NET.SilkTouch.Mods; /// extern MyEnum GetMyEnum(); /// /// -public class ExtractEnumConstants : Mod +public class ExtractEnumConstants : IMod { /// - public override async Task ExecuteAsync(IModContext ctx, CancellationToken ct = default) + public async Task ExecuteAsync(IModContext ctx, CancellationToken ct = default) { - await base.ExecuteAsync(ctx, ct); - var project = ctx.SourceProject; if (project == null) { diff --git a/sources/SilkTouch/SilkTouch/Mods/IdentifySharedPrefixes.cs b/sources/SilkTouch/SilkTouch/Mods/IdentifySharedPrefixes.cs index 34ba8ae567..fa36866f91 100644 --- a/sources/SilkTouch/SilkTouch/Mods/IdentifySharedPrefixes.cs +++ b/sources/SilkTouch/SilkTouch/Mods/IdentifySharedPrefixes.cs @@ -19,7 +19,7 @@ namespace Silk.NET.SilkTouch.Mods; /// [ModConfiguration] public class IdentifySharedPrefixes(IOptionsSnapshot config) - : Mod + : IMod { /// /// This was from the original NameTrimmer code @@ -55,7 +55,7 @@ public record Configuration } /// - public override async Task ExecuteAsync(IModContext ctx, CancellationToken ct = default) + public async Task ExecuteAsync(IModContext ctx, CancellationToken ct = default) { var configuration = config.Get(ctx.JobKey); var project = ctx.SourceProject; diff --git a/sources/SilkTouch/SilkTouch/Mods/TransformHandles.cs b/sources/SilkTouch/SilkTouch/Mods/TransformHandles.cs index b74ffba117..a337fa7c6c 100644 --- a/sources/SilkTouch/SilkTouch/Mods/TransformHandles.cs +++ b/sources/SilkTouch/SilkTouch/Mods/TransformHandles.cs @@ -32,7 +32,7 @@ namespace Silk.NET.SilkTouch.Mods; public class TransformHandles( IOptionsSnapshot config, ILogger logger -) : Mod +) : IMod { /// /// The configuration for the mod. @@ -46,10 +46,8 @@ public class Config } /// - public override async Task ExecuteAsync(IModContext ctx, CancellationToken ct = default) + public async Task ExecuteAsync(IModContext ctx, CancellationToken ct = default) { - await base.ExecuteAsync(ctx, ct); - var cfg = config.Get(ctx.JobKey); var project = ctx.SourceProject; if (project == null) From 33437dcf617d73af21594a7dded30682c94837ff Mon Sep 17 00:00:00 2001 From: William Chen Date: Mon, 8 Jun 2026 07:42:09 -0400 Subject: [PATCH 16/37] Document where ExtractFunctionPointers and ExtractNestedTypes output their extracted files Also add todo for doing the same in ExtractEnumConstants --- sources/SilkTouch/SilkTouch/Mods/ExtractEnumConstants.cs | 2 ++ sources/SilkTouch/SilkTouch/Mods/ExtractFunctionPointers.cs | 1 + sources/SilkTouch/SilkTouch/Mods/ExtractNestedTypes.cs | 1 + 3 files changed, 4 insertions(+) diff --git a/sources/SilkTouch/SilkTouch/Mods/ExtractEnumConstants.cs b/sources/SilkTouch/SilkTouch/Mods/ExtractEnumConstants.cs index 1d94daafa4..fdf4dfb369 100644 --- a/sources/SilkTouch/SilkTouch/Mods/ExtractEnumConstants.cs +++ b/sources/SilkTouch/SilkTouch/Mods/ExtractEnumConstants.cs @@ -49,6 +49,8 @@ public async Task ExecuteAsync(IModContext ctx, CancellationToken ct = default) rewriter.ConstantsToRemove = constants; rewriter.ExtractedEnums = enums.Keys; + // TODO: Place this comment where it should go after this mod works: Place extracted enum types in the directory common to where the types are referenced from + ctx.SourceProject = project; } diff --git a/sources/SilkTouch/SilkTouch/Mods/ExtractFunctionPointers.cs b/sources/SilkTouch/SilkTouch/Mods/ExtractFunctionPointers.cs index 805b464aec..e57deb1d37 100644 --- a/sources/SilkTouch/SilkTouch/Mods/ExtractFunctionPointers.cs +++ b/sources/SilkTouch/SilkTouch/Mods/ExtractFunctionPointers.cs @@ -88,6 +88,7 @@ public async Task ExecuteAsync(IModContext ctx, CancellationToken ct = default) ) : SingletonList(typeDecl) ), + // Place extracted function pointer types in the directory common to where the types are referenced from filePath: project.FullPath($"{dir}/{identifier}.gen.cs") ) .Project; diff --git a/sources/SilkTouch/SilkTouch/Mods/ExtractNestedTypes.cs b/sources/SilkTouch/SilkTouch/Mods/ExtractNestedTypes.cs index f1be8f91e0..b230d48729 100644 --- a/sources/SilkTouch/SilkTouch/Mods/ExtractNestedTypes.cs +++ b/sources/SilkTouch/SilkTouch/Mods/ExtractNestedTypes.cs @@ -66,6 +66,7 @@ rewriter.Namespace is not null ) : SingletonList(newStruct) ), + // Place extracted struct next to the original file it came from filePath: project.FullPath( $"{file.AsSpan()[..file.LastIndexOf('/')]}/{newStruct.Identifier}.gen.cs" ) From 725edda33f10cfca037e193e8f0855a49742abf0 Mon Sep 17 00:00:00 2001 From: William Chen Date: Tue, 9 Jun 2026 00:37:58 -0400 Subject: [PATCH 17/37] Get ExtractEnumConstants working --- .../SilkTouch/Mods/ExtractEnumConstants.cs | 194 +++++++++++------- 1 file changed, 124 insertions(+), 70 deletions(-) diff --git a/sources/SilkTouch/SilkTouch/Mods/ExtractEnumConstants.cs b/sources/SilkTouch/SilkTouch/Mods/ExtractEnumConstants.cs index fdf4dfb369..cb25d61081 100644 --- a/sources/SilkTouch/SilkTouch/Mods/ExtractEnumConstants.cs +++ b/sources/SilkTouch/SilkTouch/Mods/ExtractEnumConstants.cs @@ -5,6 +5,7 @@ using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Silk.NET.SilkTouch.Naming; +using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; namespace Silk.NET.SilkTouch.Mods; @@ -34,13 +35,14 @@ public async Task ExecuteAsync(IModContext ctx, CancellationToken ct = default) var walker = new Walker(); foreach (var doc in project.Documents) { - var (fname, node) = (doc.RelativePath(), await doc.GetSyntaxRootAsync(ct)); - if (fname is null) + var file = doc.RelativePath(); + if (file is null) { continue; } - walker.File = fname; + var node = await doc.GetSyntaxRootAsync(ct); + walker.File = file; walker.Visit(node); } @@ -48,12 +50,114 @@ public async Task ExecuteAsync(IModContext ctx, CancellationToken ct = default) var (enums, constants) = walker.GetExtractedEnums(); rewriter.ConstantsToRemove = constants; rewriter.ExtractedEnums = enums.Keys; + foreach (var docId in project.DocumentIds) + { + var doc = + project.GetDocument(docId) + ?? throw new InvalidOperationException("Document missing"); + + var file = doc.RelativePath(); + if (file is null) + { + continue; + } + + project = doc.WithSyntaxRoot( + rewriter.Visit(await doc.GetSyntaxRootAsync(ct)) + ?? throw new InvalidOperationException("Visit returned null.") + ).Project; + } + + var newEnums = enums.Select(x => + ( + (MemberDeclarationSyntax)x.Value.Item1, + x.Value.Item1.Identifier.ToString(), + x.Value.Item2, + x.Value.Item3 + ) + ); - // TODO: Place this comment where it should go after this mod works: Place extracted enum types in the directory common to where the types are referenced from + foreach (var (typeDecl, identifier, fileDirs, namespaces) in newEnums) + { + var ns = NameUtils.FindCommonPrefix(namespaces, true, false, true); + var dir = NameUtils.FindCommonPrefix(fileDirs, true, false, true).TrimEnd('/'); + project = project + ?.AddDocument( + $"{identifier}.gen.cs", + CompilationUnit() + .WithMembers( + ns is { Length: > 0 } + ? SingletonList( + FileScopedNamespaceDeclaration( + ModUtils.NamespaceIntoIdentifierName(ns.TrimEnd('.')) + ) + .WithMembers(SingletonList(typeDecl)) + ) + : SingletonList(typeDecl) + ), + // Place extracted enum types in the directory common to where the types are referenced from + filePath: project.FullPath($"{dir}/{identifier}.gen.cs") + ) + .Project; + } ctx.SourceProject = project; } + private static ReadOnlySpan GetNativeTypeNameForPredefinedType( + PredefinedTypeSyntax node, + Dictionary, HashSet)?>? numericTypeNames = null + ) + { + // Walk up to the parameter or method. We only allow primitive integer types right now. + var current = node.Parent; + var indirectionLevels = 0; + while (current is PointerTypeSyntax) + { + indirectionLevels++; + current = current.Parent; + } + + var attrs = current switch + { + MethodDeclarationSyntax meth => meth.AttributeLists, + ParameterSyntax param => param.AttributeLists, + _ => default, + }; + + if (attrs.Count == 0) + { + return default; + } + + if (!attrs.TryParseNativeTypeName(out var info)) + { + return null; + } + + // Ensure that the indirection levels indicated by the type name is the same as we've encountered when walking + // up the type. If this isn't, this indicates that the native type name is a typedef to a pointer and shouldn't + // be something that is mapped into an enum. + if (info.IndirectionLevels == indirectionLevels) + { + return info.Name; + } + + InvalidateIfSeen(numericTypeNames, info.Name); + return null; + } + + private static void InvalidateIfSeen( + Dictionary, HashSet)?>? numericTypeNames, + string nativeTypeName + ) + { + if (numericTypeNames?.ContainsKey(nativeTypeName) ?? false) + { + numericTypeNames[nativeTypeName] = null; + } + } + private class Walker : CSharpSyntaxRewriter { private readonly Dictionary< @@ -142,14 +246,9 @@ HashSet ExtractedConstants (EnumDeclarationSyntax, HashSet, HashSet)? extractedEnum = enumType is { } theType ? ( - SyntaxFactory - .EnumDeclaration(enumName) - .AddBaseListTypes( - SyntaxFactory.SimpleBaseType( - SyntaxFactory.PredefinedType(SyntaxFactory.Token(theType.Type)) - ) - ) - .AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword)), + EnumDeclaration(enumName) + .AddBaseListTypes(SimpleBaseType(PredefinedType(Token(theType.Type)))) + .AddModifiers(Token(SyntaxKind.PublicKeyword)), theType.ReferencingFileDirs, theType.ReferencingNamespaces ) @@ -212,9 +311,8 @@ var constCandidate in (ReadOnlySpan)[constant, trimmingName] } theExtractedEnum.Item1 = theExtractedEnum.Item1.AddMembers( - SyntaxFactory - .EnumMemberDeclaration(constant) - .WithEqualsValue(SyntaxFactory.EqualsValueClause(value)) + EnumMemberDeclaration(constant) + .WithEqualsValue(EqualsValueClause(value)) ); extractedEnum = theExtractedEnum; break; @@ -268,61 +366,6 @@ var constant in (IEnumerable) InvalidateIfSeen(_numericTypeNames, node.Identifier.ToString()); return base.VisitEnumDeclaration(node); } - - private static ReadOnlySpan GetNativeTypeNameForPredefinedType( - PredefinedTypeSyntax node, - Dictionary, HashSet)?>? numericTypeNames = - null - ) - { - // Walk up to the parameter or method. We only allow primitive integer types right now. - var current = node.Parent; - var indirectionLevels = 0; - while (current is PointerTypeSyntax) - { - indirectionLevels++; - current = current.Parent; - } - - var attrs = current switch - { - MethodDeclarationSyntax meth => meth.AttributeLists, - ParameterSyntax param => param.AttributeLists, - _ => default, - }; - - if (attrs.Count == 0) - { - return default; - } - - if (!attrs.TryParseNativeTypeName(out var info)) - { - return null; - } - - // Ensure that the indirection levels indicated by the type name is the same as we've encountered when walking - // up the type. If this isn't, this indicates that the native type name is a typedef to a pointer and shouldn't - // be something that is mapped into an enum. - if (info.IndirectionLevels == indirectionLevels) - { - return info.Name; - } - - InvalidateIfSeen(numericTypeNames, info.Name); - return null; - } - - private static void InvalidateIfSeen( - Dictionary, HashSet)?>? numericTypeNames, - string nativeTypeName - ) - { - if (numericTypeNames?.ContainsKey(nativeTypeName) ?? false) - { - numericTypeNames[nativeTypeName] = null; - } - } } private class Rewriter : CSharpSyntaxRewriter @@ -330,6 +373,17 @@ private class Rewriter : CSharpSyntaxRewriter public IReadOnlyCollection? ConstantsToRemove { get; set; } public IReadOnlyCollection? ExtractedEnums { get; set; } + public override SyntaxNode? VisitPredefinedType(PredefinedTypeSyntax node) + { + var nativeTypeName = GetNativeTypeNameForPredefinedType(node).ToString(); + if (ExtractedEnums?.Contains(nativeTypeName) ?? false) + { + return IdentifierName(nativeTypeName).WithTriviaFrom(node); + } + + return base.VisitPredefinedType(node); + } + public override SyntaxNode? VisitFieldDeclaration(FieldDeclarationSyntax node) { var ret = base.VisitFieldDeclaration(node) as FieldDeclarationSyntax; From 95d88c8054434a2e97580d195c219fef5f48c4d4 Mon Sep 17 00:00:00 2001 From: William Chen Date: Tue, 9 Jun 2026 00:51:35 -0400 Subject: [PATCH 18/37] Update config --- generator.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/generator.json b/generator.json index e1303853a0..f5cc22acac 100644 --- a/generator.json +++ b/generator.json @@ -44,6 +44,8 @@ "MarkNativeNames", "ExtractHandles", "ExtractNestedTypes", + "ExtractEnumConstants", + "ExtractFunctionPointers", "TransformHandles", "TransformFunctions", "TransformProperties", @@ -255,6 +257,8 @@ "MixKhronosData", "ExtractHandles", "ExtractNestedTypes", + "ExtractEnumConstants", + "ExtractFunctionPointers", "TransformHandles", "InterceptNativeFunctions", "TransformFunctions", @@ -398,6 +402,8 @@ "MarkNativeNames", "ExtractHandles", "ExtractNestedTypes", + "ExtractEnumConstants", + "ExtractFunctionPointers", "TransformHandles", "MixKhronosData", "AddApiProfiles", From 425f57567f97f15981e560fad8fc0b7cf7d02524 Mon Sep 17 00:00:00 2001 From: William Chen Date: Tue, 9 Jun 2026 01:03:56 -0400 Subject: [PATCH 19/37] Add remark comment on ExtractNestedTypes only handling ClangScraper's special structs --- sources/SilkTouch/SilkTouch/Mods/ExtractNestedTypes.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sources/SilkTouch/SilkTouch/Mods/ExtractNestedTypes.cs b/sources/SilkTouch/SilkTouch/Mods/ExtractNestedTypes.cs index b230d48729..d7f28816b0 100644 --- a/sources/SilkTouch/SilkTouch/Mods/ExtractNestedTypes.cs +++ b/sources/SilkTouch/SilkTouch/Mods/ExtractNestedTypes.cs @@ -15,6 +15,10 @@ namespace Silk.NET.SilkTouch.Mods; /// Extracts fixed buffers and anonymous structs output by /// into their own files as non-nested structs. /// +/// +/// Apparently this mod only handles the special nested structs output by +/// right now. We might change this in the future if there is the need. +/// public partial class ExtractNestedTypes : IMod { /// From 18f2e21cca60bb3936eadf63a77d0d6f2ca64020 Mon Sep 17 00:00:00 2001 From: William Chen Date: Tue, 9 Jun 2026 01:09:01 -0400 Subject: [PATCH 20/37] Generate on Windows (apparently accidentally fixed a namespace bug) --- sources/SDL/SDL/SDL3/VirtualJoystickDescCleanup.gen.cs | 2 ++ sources/SDL/SDL/SDL3/VirtualJoystickDescCleanupDelegate.gen.cs | 2 ++ sources/SDL/SDL/SDL3/VirtualJoystickDescRumble.gen.cs | 2 ++ sources/SDL/SDL/SDL3/VirtualJoystickDescRumbleDelegate.gen.cs | 2 ++ sources/SDL/SDL/SDL3/VirtualJoystickDescRumbleTriggers.gen.cs | 2 ++ .../SDL/SDL3/VirtualJoystickDescRumbleTriggersDelegate.gen.cs | 2 ++ sources/SDL/SDL/SDL3/VirtualJoystickDescSendEffect.gen.cs | 2 ++ .../SDL/SDL/SDL3/VirtualJoystickDescSendEffectDelegate.gen.cs | 2 ++ sources/SDL/SDL/SDL3/VirtualJoystickDescSetLed.gen.cs | 2 ++ sources/SDL/SDL/SDL3/VirtualJoystickDescSetLedDelegate.gen.cs | 2 ++ sources/SDL/SDL/SDL3/VirtualJoystickDescSetPlayerIndex.gen.cs | 2 ++ .../SDL/SDL3/VirtualJoystickDescSetPlayerIndexDelegate.gen.cs | 2 ++ .../SDL/SDL/SDL3/VirtualJoystickDescSetSensorsEnabled.gen.cs | 2 ++ .../SDL3/VirtualJoystickDescSetSensorsEnabledDelegate.gen.cs | 2 ++ sources/SDL/SDL/SDL3/VirtualJoystickDescUpdate.gen.cs | 2 ++ sources/SDL/SDL/SDL3/VirtualJoystickDescUpdateDelegate.gen.cs | 2 ++ 16 files changed, 32 insertions(+) diff --git a/sources/SDL/SDL/SDL3/VirtualJoystickDescCleanup.gen.cs b/sources/SDL/SDL/SDL3/VirtualJoystickDescCleanup.gen.cs index b492776668..5249ca465f 100644 --- a/sources/SDL/SDL/SDL3/VirtualJoystickDescCleanup.gen.cs +++ b/sources/SDL/SDL/SDL3/VirtualJoystickDescCleanup.gen.cs @@ -6,6 +6,8 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +namespace Silk.NET.SDL; + [NativeName("SDL_VirtualJoystickDesc_Cleanup")] public readonly unsafe struct VirtualJoystickDescCleanup : IDisposable { diff --git a/sources/SDL/SDL/SDL3/VirtualJoystickDescCleanupDelegate.gen.cs b/sources/SDL/SDL/SDL3/VirtualJoystickDescCleanupDelegate.gen.cs index 6a99135605..c0bc9260bd 100644 --- a/sources/SDL/SDL/SDL3/VirtualJoystickDescCleanupDelegate.gen.cs +++ b/sources/SDL/SDL/SDL3/VirtualJoystickDescCleanupDelegate.gen.cs @@ -6,5 +6,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +namespace Silk.NET.SDL; + [NativeName("SDL_VirtualJoystickDesc_Cleanup")] public unsafe delegate void VirtualJoystickDescCleanupDelegate(void* arg0); diff --git a/sources/SDL/SDL/SDL3/VirtualJoystickDescRumble.gen.cs b/sources/SDL/SDL/SDL3/VirtualJoystickDescRumble.gen.cs index 5e8c087e46..a614f6c48e 100644 --- a/sources/SDL/SDL/SDL3/VirtualJoystickDescRumble.gen.cs +++ b/sources/SDL/SDL/SDL3/VirtualJoystickDescRumble.gen.cs @@ -6,6 +6,8 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +namespace Silk.NET.SDL; + [NativeName("SDL_VirtualJoystickDesc_Rumble")] public readonly unsafe struct VirtualJoystickDescRumble : IDisposable { diff --git a/sources/SDL/SDL/SDL3/VirtualJoystickDescRumbleDelegate.gen.cs b/sources/SDL/SDL/SDL3/VirtualJoystickDescRumbleDelegate.gen.cs index 3ed4f4bdc8..37b1626b64 100644 --- a/sources/SDL/SDL/SDL3/VirtualJoystickDescRumbleDelegate.gen.cs +++ b/sources/SDL/SDL/SDL3/VirtualJoystickDescRumbleDelegate.gen.cs @@ -6,5 +6,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +namespace Silk.NET.SDL; + [NativeName("SDL_VirtualJoystickDesc_Rumble")] public unsafe delegate byte VirtualJoystickDescRumbleDelegate(void* arg0, ushort arg1, ushort arg2); diff --git a/sources/SDL/SDL/SDL3/VirtualJoystickDescRumbleTriggers.gen.cs b/sources/SDL/SDL/SDL3/VirtualJoystickDescRumbleTriggers.gen.cs index bf5f71aa6b..072173e604 100644 --- a/sources/SDL/SDL/SDL3/VirtualJoystickDescRumbleTriggers.gen.cs +++ b/sources/SDL/SDL/SDL3/VirtualJoystickDescRumbleTriggers.gen.cs @@ -6,6 +6,8 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +namespace Silk.NET.SDL; + [NativeName("SDL_VirtualJoystickDesc_RumbleTriggers")] public readonly unsafe struct VirtualJoystickDescRumbleTriggers : IDisposable { diff --git a/sources/SDL/SDL/SDL3/VirtualJoystickDescRumbleTriggersDelegate.gen.cs b/sources/SDL/SDL/SDL3/VirtualJoystickDescRumbleTriggersDelegate.gen.cs index fc133d07b8..3578e6b51b 100644 --- a/sources/SDL/SDL/SDL3/VirtualJoystickDescRumbleTriggersDelegate.gen.cs +++ b/sources/SDL/SDL/SDL3/VirtualJoystickDescRumbleTriggersDelegate.gen.cs @@ -6,6 +6,8 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +namespace Silk.NET.SDL; + [NativeName("SDL_VirtualJoystickDesc_RumbleTriggers")] public unsafe delegate byte VirtualJoystickDescRumbleTriggersDelegate( void* arg0, diff --git a/sources/SDL/SDL/SDL3/VirtualJoystickDescSendEffect.gen.cs b/sources/SDL/SDL/SDL3/VirtualJoystickDescSendEffect.gen.cs index 5966a79a12..8ed1212912 100644 --- a/sources/SDL/SDL/SDL3/VirtualJoystickDescSendEffect.gen.cs +++ b/sources/SDL/SDL/SDL3/VirtualJoystickDescSendEffect.gen.cs @@ -6,6 +6,8 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +namespace Silk.NET.SDL; + [NativeName("SDL_VirtualJoystickDesc_SendEffect")] public readonly unsafe struct VirtualJoystickDescSendEffect : IDisposable { diff --git a/sources/SDL/SDL/SDL3/VirtualJoystickDescSendEffectDelegate.gen.cs b/sources/SDL/SDL/SDL3/VirtualJoystickDescSendEffectDelegate.gen.cs index ee675138b3..6150e24b48 100644 --- a/sources/SDL/SDL/SDL3/VirtualJoystickDescSendEffectDelegate.gen.cs +++ b/sources/SDL/SDL/SDL3/VirtualJoystickDescSendEffectDelegate.gen.cs @@ -6,5 +6,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +namespace Silk.NET.SDL; + [NativeName("SDL_VirtualJoystickDesc_SendEffect")] public unsafe delegate byte VirtualJoystickDescSendEffectDelegate(void* arg0, void* arg1, int arg2); diff --git a/sources/SDL/SDL/SDL3/VirtualJoystickDescSetLed.gen.cs b/sources/SDL/SDL/SDL3/VirtualJoystickDescSetLed.gen.cs index 850d070382..690c9f121b 100644 --- a/sources/SDL/SDL/SDL3/VirtualJoystickDescSetLed.gen.cs +++ b/sources/SDL/SDL/SDL3/VirtualJoystickDescSetLed.gen.cs @@ -6,6 +6,8 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +namespace Silk.NET.SDL; + [NativeName("SDL_VirtualJoystickDesc_SetLED")] public readonly unsafe struct VirtualJoystickDescSetLed : IDisposable { diff --git a/sources/SDL/SDL/SDL3/VirtualJoystickDescSetLedDelegate.gen.cs b/sources/SDL/SDL/SDL3/VirtualJoystickDescSetLedDelegate.gen.cs index e1e865335e..af1146aa34 100644 --- a/sources/SDL/SDL/SDL3/VirtualJoystickDescSetLedDelegate.gen.cs +++ b/sources/SDL/SDL/SDL3/VirtualJoystickDescSetLedDelegate.gen.cs @@ -6,6 +6,8 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +namespace Silk.NET.SDL; + [NativeName("SDL_VirtualJoystickDesc_SetLED")] public unsafe delegate byte VirtualJoystickDescSetLedDelegate( void* arg0, diff --git a/sources/SDL/SDL/SDL3/VirtualJoystickDescSetPlayerIndex.gen.cs b/sources/SDL/SDL/SDL3/VirtualJoystickDescSetPlayerIndex.gen.cs index 12a83b4781..462e1d1b26 100644 --- a/sources/SDL/SDL/SDL3/VirtualJoystickDescSetPlayerIndex.gen.cs +++ b/sources/SDL/SDL/SDL3/VirtualJoystickDescSetPlayerIndex.gen.cs @@ -6,6 +6,8 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +namespace Silk.NET.SDL; + [NativeName("SDL_VirtualJoystickDesc_SetPlayerIndex")] public readonly unsafe struct VirtualJoystickDescSetPlayerIndex : IDisposable { diff --git a/sources/SDL/SDL/SDL3/VirtualJoystickDescSetPlayerIndexDelegate.gen.cs b/sources/SDL/SDL/SDL3/VirtualJoystickDescSetPlayerIndexDelegate.gen.cs index ddd3eadd90..5eb9da79b2 100644 --- a/sources/SDL/SDL/SDL3/VirtualJoystickDescSetPlayerIndexDelegate.gen.cs +++ b/sources/SDL/SDL/SDL3/VirtualJoystickDescSetPlayerIndexDelegate.gen.cs @@ -6,5 +6,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +namespace Silk.NET.SDL; + [NativeName("SDL_VirtualJoystickDesc_SetPlayerIndex")] public unsafe delegate void VirtualJoystickDescSetPlayerIndexDelegate(void* arg0, int arg1); diff --git a/sources/SDL/SDL/SDL3/VirtualJoystickDescSetSensorsEnabled.gen.cs b/sources/SDL/SDL/SDL3/VirtualJoystickDescSetSensorsEnabled.gen.cs index 8174ae5f5c..f88157f11d 100644 --- a/sources/SDL/SDL/SDL3/VirtualJoystickDescSetSensorsEnabled.gen.cs +++ b/sources/SDL/SDL/SDL3/VirtualJoystickDescSetSensorsEnabled.gen.cs @@ -6,6 +6,8 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +namespace Silk.NET.SDL; + [NativeName("SDL_VirtualJoystickDesc_SetSensorsEnabled")] public readonly unsafe struct VirtualJoystickDescSetSensorsEnabled : IDisposable { diff --git a/sources/SDL/SDL/SDL3/VirtualJoystickDescSetSensorsEnabledDelegate.gen.cs b/sources/SDL/SDL/SDL3/VirtualJoystickDescSetSensorsEnabledDelegate.gen.cs index 706cecf759..f53ea42f08 100644 --- a/sources/SDL/SDL/SDL3/VirtualJoystickDescSetSensorsEnabledDelegate.gen.cs +++ b/sources/SDL/SDL/SDL3/VirtualJoystickDescSetSensorsEnabledDelegate.gen.cs @@ -6,5 +6,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +namespace Silk.NET.SDL; + [NativeName("SDL_VirtualJoystickDesc_SetSensorsEnabled")] public unsafe delegate byte VirtualJoystickDescSetSensorsEnabledDelegate(void* arg0, byte arg1); diff --git a/sources/SDL/SDL/SDL3/VirtualJoystickDescUpdate.gen.cs b/sources/SDL/SDL/SDL3/VirtualJoystickDescUpdate.gen.cs index cdc8c7c4e6..7824302a89 100644 --- a/sources/SDL/SDL/SDL3/VirtualJoystickDescUpdate.gen.cs +++ b/sources/SDL/SDL/SDL3/VirtualJoystickDescUpdate.gen.cs @@ -6,6 +6,8 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +namespace Silk.NET.SDL; + [NativeName("SDL_VirtualJoystickDesc_Update")] public readonly unsafe struct VirtualJoystickDescUpdate : IDisposable { diff --git a/sources/SDL/SDL/SDL3/VirtualJoystickDescUpdateDelegate.gen.cs b/sources/SDL/SDL/SDL3/VirtualJoystickDescUpdateDelegate.gen.cs index f44e1e27f1..62830392e5 100644 --- a/sources/SDL/SDL/SDL3/VirtualJoystickDescUpdateDelegate.gen.cs +++ b/sources/SDL/SDL/SDL3/VirtualJoystickDescUpdateDelegate.gen.cs @@ -6,5 +6,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +namespace Silk.NET.SDL; + [NativeName("SDL_VirtualJoystickDesc_Update")] public unsafe delegate void VirtualJoystickDescUpdateDelegate(void* arg0); From 626fb61c1fae5af2a06cefef150ae6f3a2b45701 Mon Sep 17 00:00:00 2001 From: William Chen Date: Tue, 9 Jun 2026 03:34:57 -0400 Subject: [PATCH 21/37] Remove unused code --- .../SilkTouch/Mods/ExtractEnumConstants.cs | 23 +------------------ 1 file changed, 1 insertion(+), 22 deletions(-) diff --git a/sources/SilkTouch/SilkTouch/Mods/ExtractEnumConstants.cs b/sources/SilkTouch/SilkTouch/Mods/ExtractEnumConstants.cs index cb25d61081..08f496a2c4 100644 --- a/sources/SilkTouch/SilkTouch/Mods/ExtractEnumConstants.cs +++ b/sources/SilkTouch/SilkTouch/Mods/ExtractEnumConstants.cs @@ -104,10 +104,7 @@ public async Task ExecuteAsync(IModContext ctx, CancellationToken ct = default) ctx.SourceProject = project; } - private static ReadOnlySpan GetNativeTypeNameForPredefinedType( - PredefinedTypeSyntax node, - Dictionary, HashSet)?>? numericTypeNames = null - ) + private static ReadOnlySpan GetNativeTypeNameForPredefinedType(PredefinedTypeSyntax node) { // Walk up to the parameter or method. We only allow primitive integer types right now. var current = node.Parent; @@ -143,21 +140,9 @@ private static ReadOnlySpan GetNativeTypeNameForPredefinedType( return info.Name; } - InvalidateIfSeen(numericTypeNames, info.Name); return null; } - private static void InvalidateIfSeen( - Dictionary, HashSet)?>? numericTypeNames, - string nativeTypeName - ) - { - if (numericTypeNames?.ContainsKey(nativeTypeName) ?? false) - { - numericTypeNames[nativeTypeName] = null; - } - } - private class Walker : CSharpSyntaxRewriter { private readonly Dictionary< @@ -360,12 +345,6 @@ var constant in (IEnumerable) } return base.VisitFieldDeclaration(node); } - - public override SyntaxNode? VisitEnumDeclaration(EnumDeclarationSyntax node) - { - InvalidateIfSeen(_numericTypeNames, node.Identifier.ToString()); - return base.VisitEnumDeclaration(node); - } } private class Rewriter : CSharpSyntaxRewriter From 6b9108d7392e274a467f40e27bdb371a91fd0eaf Mon Sep 17 00:00:00 2001 From: William Chen Date: Tue, 9 Jun 2026 03:36:35 -0400 Subject: [PATCH 22/37] Cleanup GetNativeTypeNameForPredefinedType --- sources/SilkTouch/SilkTouch/Mods/ExtractEnumConstants.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sources/SilkTouch/SilkTouch/Mods/ExtractEnumConstants.cs b/sources/SilkTouch/SilkTouch/Mods/ExtractEnumConstants.cs index 08f496a2c4..174fd05c43 100644 --- a/sources/SilkTouch/SilkTouch/Mods/ExtractEnumConstants.cs +++ b/sources/SilkTouch/SilkTouch/Mods/ExtractEnumConstants.cs @@ -129,18 +129,18 @@ private static ReadOnlySpan GetNativeTypeNameForPredefinedType(PredefinedT if (!attrs.TryParseNativeTypeName(out var info)) { - return null; + return default; } // Ensure that the indirection levels indicated by the type name is the same as we've encountered when walking // up the type. If this isn't, this indicates that the native type name is a typedef to a pointer and shouldn't // be something that is mapped into an enum. - if (info.IndirectionLevels == indirectionLevels) + if (info.IndirectionLevels != indirectionLevels) { - return info.Name; + return default; } - return null; + return info.Name; } private class Walker : CSharpSyntaxRewriter From 02ce5e38673a03ab96ce9cc889d8a46d0c7812df Mon Sep 17 00:00:00 2001 From: William Chen Date: Tue, 9 Jun 2026 03:40:36 -0400 Subject: [PATCH 23/37] Use constructor and avoid nullable properties We always allocate these so this is strictly a readability and performance improvement. --- .../SilkTouch/Mods/ExtractEnumConstants.cs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/sources/SilkTouch/SilkTouch/Mods/ExtractEnumConstants.cs b/sources/SilkTouch/SilkTouch/Mods/ExtractEnumConstants.cs index 174fd05c43..cbca82fd5c 100644 --- a/sources/SilkTouch/SilkTouch/Mods/ExtractEnumConstants.cs +++ b/sources/SilkTouch/SilkTouch/Mods/ExtractEnumConstants.cs @@ -46,10 +46,8 @@ public async Task ExecuteAsync(IModContext ctx, CancellationToken ct = default) walker.Visit(node); } - var rewriter = new Rewriter(); var (enums, constants) = walker.GetExtractedEnums(); - rewriter.ConstantsToRemove = constants; - rewriter.ExtractedEnums = enums.Keys; + var rewriter = new Rewriter(constants, enums.Keys); foreach (var docId in project.DocumentIds) { var doc = @@ -347,15 +345,15 @@ var constant in (IEnumerable) } } - private class Rewriter : CSharpSyntaxRewriter + private class Rewriter( + IReadOnlyCollection constantsToRemove, + IReadOnlyCollection extractedEnums + ) : CSharpSyntaxRewriter { - public IReadOnlyCollection? ConstantsToRemove { get; set; } - public IReadOnlyCollection? ExtractedEnums { get; set; } - public override SyntaxNode? VisitPredefinedType(PredefinedTypeSyntax node) { var nativeTypeName = GetNativeTypeNameForPredefinedType(node).ToString(); - if (ExtractedEnums?.Contains(nativeTypeName) ?? false) + if (extractedEnums.Contains(nativeTypeName)) { return IdentifierName(nativeTypeName).WithTriviaFrom(node); } @@ -371,10 +369,11 @@ private class Rewriter : CSharpSyntaxRewriter public override SyntaxNode? VisitVariableDeclarator(VariableDeclaratorSyntax node) { - if (ConstantsToRemove?.Contains(node.Identifier.ToString()) ?? false) + if (constantsToRemove.Contains(node.Identifier.ToString())) { return null; } + return base.VisitVariableDeclarator(node); } } From 0531153f2104a180f35578bccbd07bb50f72287b Mon Sep 17 00:00:00 2001 From: William Chen Date: Tue, 9 Jun 2026 04:10:11 -0400 Subject: [PATCH 24/37] Refactor code to use record structs --- .../SilkTouch/Mods/ExtractEnumConstants.cs | 59 ++++++++++--------- .../SilkTouch/Mods/ExtractFunctionPointers.cs | 34 +++++++---- 2 files changed, 53 insertions(+), 40 deletions(-) diff --git a/sources/SilkTouch/SilkTouch/Mods/ExtractEnumConstants.cs b/sources/SilkTouch/SilkTouch/Mods/ExtractEnumConstants.cs index cbca82fd5c..21238e50c8 100644 --- a/sources/SilkTouch/SilkTouch/Mods/ExtractEnumConstants.cs +++ b/sources/SilkTouch/SilkTouch/Mods/ExtractEnumConstants.cs @@ -66,14 +66,12 @@ public async Task ExecuteAsync(IModContext ctx, CancellationToken ct = default) ).Project; } - var newEnums = enums.Select(x => - ( - (MemberDeclarationSyntax)x.Value.Item1, - x.Value.Item1.Identifier.ToString(), - x.Value.Item2, - x.Value.Item3 - ) - ); + var newEnums = enums.Select(x => new ExtractedType( + x.Value.Item1, + x.Value.Item1.Identifier.ToString(), + x.Value.Item2, + x.Value.Item3 + )); foreach (var (typeDecl, identifier, fileDirs, namespaces) in newEnums) { @@ -141,16 +139,30 @@ private static ReadOnlySpan GetNativeTypeNameForPredefinedType(PredefinedT return info.Name; } + private record struct ExtractedType( + MemberDeclarationSyntax Node, + string Identifier, + HashSet ReferencingFileDirs, + HashSet ReferencingNamespaces + ); + + // TODO: Figure out what these actually mean + private record struct A( + SyntaxKind Type, + HashSet ReferencingFileDirs, + HashSet ReferencingNamespaces + ); + + // TODO: Figure out what these actually mean + private record struct B( + EnumDeclarationSyntax Item1, + HashSet Item2, + HashSet Item3 + ); + private class Walker : CSharpSyntaxRewriter { - private readonly Dictionary< - string, - ( - SyntaxKind Type, - HashSet ReferencingFileDirs, - HashSet ReferencingNamespaces - )? - > _numericTypeNames = new(); + private readonly Dictionary _numericTypeNames = new(); /// /// Tracks the name and value of constants discovered. @@ -204,19 +216,13 @@ or SyntaxKind.ULongKeyword // This code can probably be better. public ( - Dictionary< - string, - (EnumDeclarationSyntax, HashSet, HashSet) - > ExtractedEnums, + Dictionary ExtractedEnums, HashSet ExtractedConstants ) GetExtractedEnums() { var ineligibleConstants = new HashSet(); var extractedConstants = new HashSet(); - var extractedEnums = new Dictionary< - string, - (EnumDeclarationSyntax, HashSet, HashSet) - >(_numericTypeNames.Count); + var extractedEnums = new Dictionary(_numericTypeNames.Count); // Try and find constants for each of the enums we've found. // We do this in descending length order to ensure that we find the longest match for constant names to enum @@ -226,9 +232,8 @@ HashSet ExtractedConstants ) { var enumTrimmingName = NameSplitter.Underscore(enumName); - (EnumDeclarationSyntax, HashSet, HashSet)? extractedEnum = enumType - is { } theType - ? ( + B? extractedEnum = enumType is { } theType + ? new B( EnumDeclaration(enumName) .AddBaseListTypes(SimpleBaseType(PredefinedType(Token(theType.Type)))) .AddModifiers(Token(SyntaxKind.PublicKeyword)), diff --git a/sources/SilkTouch/SilkTouch/Mods/ExtractFunctionPointers.cs b/sources/SilkTouch/SilkTouch/Mods/ExtractFunctionPointers.cs index e57deb1d37..209962030e 100644 --- a/sources/SilkTouch/SilkTouch/Mods/ExtractFunctionPointers.cs +++ b/sources/SilkTouch/SilkTouch/Mods/ExtractFunctionPointers.cs @@ -52,15 +52,15 @@ public async Task ExecuteAsync(IModContext ctx, CancellationToken ct = default) // This is moved out of the foreach statement for better debuggability var extractedFunctionPointers = rewriter .FunctionPointerTypes.Values.SelectMany(x => - (IEnumerable<(MemberDeclarationSyntax, string, HashSet, HashSet)>) + (IEnumerable) [ - ( + new ExtractedType( x.Delegate, x.Delegate.Identifier.ToString(), x.ReferencingFileDirs, x.ReferencingNamespaces ), - ( + new ExtractedType( x.Pfn, x.Pfn.Identifier.ToString(), x.ReferencingFileDirs, @@ -97,20 +97,26 @@ public async Task ExecuteAsync(IModContext ctx, CancellationToken ct = default) ctx.SourceProject = project; } + private record struct ExtractedType( + MemberDeclarationSyntax Node, + string Identifier, + HashSet ReferencingFileDirs, + HashSet ReferencingNamespaces + ); + + private record struct ExtractedFunctionPointerType( + StructDeclarationSyntax Pfn, + DelegateDeclarationSyntax Delegate, + HashSet ReferencingFileDirs, + HashSet ReferencingNamespaces + ); + private partial class Rewriter(ILogger logger) : CSharpSyntaxRewriter { private string? _typeNameFromOuterFunctionPointer; private string? _fallbackFromOuterFunctionPointer; - public Dictionary< - string, - ( - StructDeclarationSyntax Pfn, - DelegateDeclarationSyntax Delegate, - HashSet ReferencingFileDirs, - HashSet ReferencingNamespaces - ) - > FunctionPointerTypes { get; } = []; + public Dictionary FunctionPointerTypes { get; } = []; public string? File { get; set; } @@ -309,7 +315,9 @@ _fallbackFromOuterFunctionPointer is not null ), node ); - FunctionPointerTypes[currentNativeTypeName] = pfnInfo = (pfn, @delegate, [], []); + + FunctionPointerTypes[currentNativeTypeName] = pfnInfo = + new ExtractedFunctionPointerType(pfn, @delegate, [], []); } // Ensure this visitation is used to determine the namespace/location. From 1f62ae06a9f2c21ecdc8b168e3453ab82e28e14a Mon Sep 17 00:00:00 2001 From: William Chen Date: Tue, 9 Jun 2026 04:10:45 -0400 Subject: [PATCH 25/37] Work on deciphering tuple usages --- .../SilkTouch/Mods/ExtractEnumConstants.cs | 41 +++++++++---------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/sources/SilkTouch/SilkTouch/Mods/ExtractEnumConstants.cs b/sources/SilkTouch/SilkTouch/Mods/ExtractEnumConstants.cs index 21238e50c8..68df006537 100644 --- a/sources/SilkTouch/SilkTouch/Mods/ExtractEnumConstants.cs +++ b/sources/SilkTouch/SilkTouch/Mods/ExtractEnumConstants.cs @@ -67,10 +67,10 @@ public async Task ExecuteAsync(IModContext ctx, CancellationToken ct = default) } var newEnums = enums.Select(x => new ExtractedType( - x.Value.Item1, - x.Value.Item1.Identifier.ToString(), - x.Value.Item2, - x.Value.Item3 + x.Value.Node, + x.Value.Node.Identifier.ToString(), + x.Value.ReferencingFileDirs, + x.Value.ReferencingNamespaces )); foreach (var (typeDecl, identifier, fileDirs, namespaces) in newEnums) @@ -146,22 +146,21 @@ private record struct ExtractedType( HashSet ReferencingNamespaces ); - // TODO: Figure out what these actually mean - private record struct A( - SyntaxKind Type, + private record struct ExtractedEnumType( + EnumDeclarationSyntax Node, HashSet ReferencingFileDirs, HashSet ReferencingNamespaces ); - // TODO: Figure out what these actually mean - private record struct B( - EnumDeclarationSyntax Item1, - HashSet Item2, - HashSet Item3 - ); - private class Walker : CSharpSyntaxRewriter { + // TODO: Figure out what these actually mean + private record struct A( + SyntaxKind Type, + HashSet ReferencingFileDirs, + HashSet ReferencingNamespaces + ); + private readonly Dictionary _numericTypeNames = new(); /// @@ -180,7 +179,7 @@ private class Walker : CSharpSyntaxRewriter var thisType = node.Keyword.Kind(); if (!_numericTypeNames.TryGetValue(nativeTypeName, out var numericTypeName)) { - _numericTypeNames[nativeTypeName] = numericTypeName = (thisType, [], []); + _numericTypeNames[nativeTypeName] = numericTypeName = new A(thisType, [], []); } if ( @@ -216,13 +215,13 @@ or SyntaxKind.ULongKeyword // This code can probably be better. public ( - Dictionary ExtractedEnums, + Dictionary ExtractedEnums, HashSet ExtractedConstants ) GetExtractedEnums() { var ineligibleConstants = new HashSet(); var extractedConstants = new HashSet(); - var extractedEnums = new Dictionary(_numericTypeNames.Count); + var extractedEnums = new Dictionary(_numericTypeNames.Count); // Try and find constants for each of the enums we've found. // We do this in descending length order to ensure that we find the longest match for constant names to enum @@ -232,8 +231,8 @@ HashSet ExtractedConstants ) { var enumTrimmingName = NameSplitter.Underscore(enumName); - B? extractedEnum = enumType is { } theType - ? new B( + ExtractedEnumType? extractedEnum = enumType is { } theType + ? new ExtractedEnumType( EnumDeclaration(enumName) .AddBaseListTypes(SimpleBaseType(PredefinedType(Token(theType.Type)))) .AddModifiers(Token(SyntaxKind.PublicKeyword)), @@ -298,7 +297,7 @@ var constCandidate in (ReadOnlySpan)[constant, trimmingName] break; } - theExtractedEnum.Item1 = theExtractedEnum.Item1.AddMembers( + theExtractedEnum.Node = theExtractedEnum.Node.AddMembers( EnumMemberDeclaration(constant) .WithEqualsValue(EqualsValueClause(value)) ); @@ -323,7 +322,7 @@ var constant in (IEnumerable) } ineligibleConstants.Clear(); - if (extractedEnum is { Item1.Members.Count: > 0 }) + if (extractedEnum is { Node.Members.Count: > 0 }) { extractedEnums[enumName] = extractedEnum.Value; } From 8b73fee828711dd8c67a565504f177480bb46326 Mon Sep 17 00:00:00 2001 From: William Chen Date: Tue, 9 Jun 2026 04:20:02 -0400 Subject: [PATCH 26/37] Figure out what _numericTypeNames does --- .../SilkTouch/Mods/ExtractEnumConstants.cs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/sources/SilkTouch/SilkTouch/Mods/ExtractEnumConstants.cs b/sources/SilkTouch/SilkTouch/Mods/ExtractEnumConstants.cs index 68df006537..1c25c4510d 100644 --- a/sources/SilkTouch/SilkTouch/Mods/ExtractEnumConstants.cs +++ b/sources/SilkTouch/SilkTouch/Mods/ExtractEnumConstants.cs @@ -154,14 +154,18 @@ HashSet ReferencingNamespaces private class Walker : CSharpSyntaxRewriter { - // TODO: Figure out what these actually mean - private record struct A( + private record struct BackingType( SyntaxKind Type, HashSet ReferencingFileDirs, HashSet ReferencingNamespaces ); - private readonly Dictionary _numericTypeNames = new(); + /// + /// Tracks the backing type to use for identified enum types. + /// Null is used when there are more than one potential backing type + /// or if the identified backing type cannot be used as a valid C# enum backing type. + /// + private readonly Dictionary _numericTypeNames = new(); /// /// Tracks the name and value of constants discovered. @@ -179,7 +183,11 @@ HashSet ReferencingNamespaces var thisType = node.Keyword.Kind(); if (!_numericTypeNames.TryGetValue(nativeTypeName, out var numericTypeName)) { - _numericTypeNames[nativeTypeName] = numericTypeName = new A(thisType, [], []); + _numericTypeNames[nativeTypeName] = numericTypeName = new BackingType( + thisType, + [], + [] + ); } if ( From e42ca259f29bd71aa6684008a0fbac37e9cea549 Mon Sep 17 00:00:00 2001 From: William Chen Date: Tue, 9 Jun 2026 04:34:37 -0400 Subject: [PATCH 27/37] Add support for requiredTargetSpecifier to TryParseNativeTypeName --- .../SilkTouch/Mods/Common/AttributeUtils.cs | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/sources/SilkTouch/SilkTouch/Mods/Common/AttributeUtils.cs b/sources/SilkTouch/SilkTouch/Mods/Common/AttributeUtils.cs index c00cdfc2b1..fa83d1547f 100644 --- a/sources/SilkTouch/SilkTouch/Mods/Common/AttributeUtils.cs +++ b/sources/SilkTouch/SilkTouch/Mods/Common/AttributeUtils.cs @@ -314,16 +314,19 @@ .. attributes.Where(attribute => /// Retrieves the native type name within the given attribute list. /// /// The attributes. - /// The required attribute target/context. + /// + /// The required attribute target specifier. + /// Eg: for [return: Attribute]. + /// /// The native type name. public static string? GetNativeTypeName( this IEnumerable attrs, - SyntaxKind? requireContext = null + SyntaxKind? requiredTargetSpecifier = null ) => attrs .SelectMany(x => - (x.Target is null && requireContext is null) - || (requireContext is { } rc && (x.Target?.Identifier.IsKind(rc) ?? false)) + (x.Target is null && requiredTargetSpecifier is null) + || (requiredTargetSpecifier is { } rc && (x.Target?.Identifier.IsKind(rc) ?? false)) ? x.Attributes : [] ) @@ -341,16 +344,21 @@ .. attributes.Where(attribute => /// /// The attributes. /// The parsed native type info. Invalid if this method returns false. + /// + /// The required attribute target specifier. + /// Eg: for [return: Attribute]. + /// /// Whether the type name was successfully parsed. /// /// This does not handle all of the possible cases. /// public static bool TryParseNativeTypeName( this IEnumerable attrs, - out NativeTypeNameInfo info + out NativeTypeNameInfo info, + SyntaxKind? requiredTargetSpecifier = null ) { - var nativeTypeNameString = attrs.GetNativeTypeName(); + var nativeTypeNameString = attrs.GetNativeTypeName(requiredTargetSpecifier); var nativeTypeName = nativeTypeNameString.AsSpan(); if (nativeTypeName.Length == 0) { From 10aedf52dad6cb404e7ff12dcbc78c47d71dcdfb Mon Sep 17 00:00:00 2001 From: William Chen Date: Tue, 9 Jun 2026 04:41:38 -0400 Subject: [PATCH 28/37] Handle method return types properly and fix SuccessfullyExtractsCStyleEnumConstants_ReturnType --- .../SilkTouch/Mods/ExtractEnumConstants.cs | 33 +++++++++++++++---- ...StyleEnumConstants_ReturnType.verified.txt | 21 +++++++++++- 2 files changed, 46 insertions(+), 8 deletions(-) diff --git a/sources/SilkTouch/SilkTouch/Mods/ExtractEnumConstants.cs b/sources/SilkTouch/SilkTouch/Mods/ExtractEnumConstants.cs index 1c25c4510d..ee161d27be 100644 --- a/sources/SilkTouch/SilkTouch/Mods/ExtractEnumConstants.cs +++ b/sources/SilkTouch/SilkTouch/Mods/ExtractEnumConstants.cs @@ -103,6 +103,7 @@ public async Task ExecuteAsync(IModContext ctx, CancellationToken ct = default) private static ReadOnlySpan GetNativeTypeNameForPredefinedType(PredefinedTypeSyntax node) { // Walk up to the parameter or method. We only allow primitive integer types right now. + SyntaxNode previous = node; var current = node.Parent; var indirectionLevels = 0; while (current is PointerTypeSyntax) @@ -111,19 +112,37 @@ private static ReadOnlySpan GetNativeTypeNameForPredefinedType(PredefinedT current = current.Parent; } - var attrs = current switch + SyntaxList attributes; + SyntaxKind? requiredTargetSpecifier = null; + switch (current) { - MethodDeclarationSyntax meth => meth.AttributeLists, - ParameterSyntax param => param.AttributeLists, - _ => default, - }; + // Method return type + case MethodDeclarationSyntax method: + { + attributes = method.AttributeLists; + requiredTargetSpecifier = SyntaxKind.ReturnKeyword; + + break; + } + // Method parameter + case ParameterSyntax param: + { + attributes = param.AttributeLists; + break; + } + default: + { + attributes = default; + break; + } + } - if (attrs.Count == 0) + if (attributes.Count == 0) { return default; } - if (!attrs.TryParseNativeTypeName(out var info)) + if (!attributes.TryParseNativeTypeName(out var info, requiredTargetSpecifier)) { return default; } diff --git a/tests/SilkTouch/SilkTouch/ExtractEnumConstantsTests.SuccessfullyExtractsCStyleEnumConstants_ReturnType.verified.txt b/tests/SilkTouch/SilkTouch/ExtractEnumConstantsTests.SuccessfullyExtractsCStyleEnumConstants_ReturnType.verified.txt index 5f282702bb..fcd4d92a22 100644 --- a/tests/SilkTouch/SilkTouch/ExtractEnumConstantsTests.SuccessfullyExtractsCStyleEnumConstants_ReturnType.verified.txt +++ b/tests/SilkTouch/SilkTouch/ExtractEnumConstantsTests.SuccessfullyExtractsCStyleEnumConstants_ReturnType.verified.txt @@ -1 +1,20 @@ - \ No newline at end of file +// SDL_BlendMode.gen.cs +public enum SDL_BlendMode : uint +{ + SDL_BLENDMODE_NONE = 0x00000000U, + SDL_BLENDMODE_BLEND = 0x00000001U, + SDL_BLENDMODE_BLEND_PREMULTIPLIED = 0x00000010U, + SDL_BLENDMODE_ADD = 0x00000002U, + SDL_BLENDMODE_ADD_PREMULTIPLIED = 0x00000020U, + SDL_BLENDMODE_MOD = 0x00000004U, + SDL_BLENDMODE_MUL = 0x00000008U, + SDL_BLENDMODE_INVALID = 0x7FFFFFFFU +} + +// Sdl.gen.cs +public unsafe partial struct Sdl +{ + [DllImport("SDL3", ExactSpelling = true)] + [return: NativeTypeName("SDL_BlendMode")] + public static extern SDL_BlendMode SDL_ComposeCustomBlendMode(SDL_BlendFactor srcColorFactor, SDL_BlendFactor dstColorFactor, SDL_BlendOperation colorOperation, SDL_BlendFactor srcAlphaFactor, SDL_BlendFactor dstAlphaFactor, SDL_BlendOperation alphaOperation); +} From 470301f92816d60dd2892d6aa196255c2d3d5c43 Mon Sep 17 00:00:00 2001 From: William Chen Date: Tue, 9 Jun 2026 04:42:45 -0400 Subject: [PATCH 29/37] Cleanup todos --- tests/SilkTouch/SilkTouch/ExtractEnumConstantsTests.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/SilkTouch/SilkTouch/ExtractEnumConstantsTests.cs b/tests/SilkTouch/SilkTouch/ExtractEnumConstantsTests.cs index 6766327d33..102fe5d45b 100644 --- a/tests/SilkTouch/SilkTouch/ExtractEnumConstantsTests.cs +++ b/tests/SilkTouch/SilkTouch/ExtractEnumConstantsTests.cs @@ -5,7 +5,6 @@ namespace Silk.NET.SilkTouch.UnitTests; -// TODO public class ExtractEnumConstantsTests { static ExtractEnumConstantsTests() @@ -58,7 +57,7 @@ public class Test public uint Blend; } """, - // TODO: ExtractEnumConstants requires the file path to be set and that the document is under a subfolder + // ExtractEnumConstants requires the file path to be set and that the document is under a subfolder filePath: $"SDL3/{inputDocName}" ) .Project; From 905ec5550c0294f32faeabb717dff2b212b8c561 Mon Sep 17 00:00:00 2001 From: William Chen Date: Tue, 9 Jun 2026 05:48:04 -0400 Subject: [PATCH 30/37] Handle fields properly and fix SuccessfullyExtractsCStyleEnumConstants_Field --- .../SilkTouch/Mods/ExtractEnumConstants.cs | 29 +++++++++++++++++++ ...actsCStyleEnumConstants_Field.verified.txt | 24 ++++++++++++++- 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/sources/SilkTouch/SilkTouch/Mods/ExtractEnumConstants.cs b/sources/SilkTouch/SilkTouch/Mods/ExtractEnumConstants.cs index ee161d27be..e6734dfb5a 100644 --- a/sources/SilkTouch/SilkTouch/Mods/ExtractEnumConstants.cs +++ b/sources/SilkTouch/SilkTouch/Mods/ExtractEnumConstants.cs @@ -100,6 +100,14 @@ public async Task ExecuteAsync(IModContext ctx, CancellationToken ct = default) ctx.SourceProject = project; } + /// + /// Returns the native type name for a predefined type syntax node found in the syntax tree. + /// The native type name will be retrieved from the corresponding [NativeTypeName] attribute. + /// + /// + /// This is designed to be used to find references to enum types. + /// As such, this method returns "" for native type names that are identifiable as being potential enum members. + /// private static ReadOnlySpan GetNativeTypeNameForPredefinedType(PredefinedTypeSyntax node) { // Walk up to the parameter or method. We only allow primitive integer types right now. @@ -130,6 +138,20 @@ private static ReadOnlySpan GetNativeTypeNameForPredefinedType(PredefinedT attributes = param.AttributeLists; break; } + // Maybe a field + case VariableDeclarationSyntax variable: + { + attributes = default; + if ( + variable.Parent is FieldDeclarationSyntax field + && !field.Modifiers.Any(m => m.IsKind(SyntaxKind.ConstKeyword)) + ) + { + attributes = field.AttributeLists; + } + + break; + } default: { attributes = default; @@ -147,6 +169,13 @@ private static ReadOnlySpan GetNativeTypeNameForPredefinedType(PredefinedT return default; } + // Ignore defines. + // These are likely enum members. + if (info.IsDefine) + { + return default; + } + // Ensure that the indirection levels indicated by the type name is the same as we've encountered when walking // up the type. If this isn't, this indicates that the native type name is a typedef to a pointer and shouldn't // be something that is mapped into an enum. diff --git a/tests/SilkTouch/SilkTouch/ExtractEnumConstantsTests.SuccessfullyExtractsCStyleEnumConstants_Field.verified.txt b/tests/SilkTouch/SilkTouch/ExtractEnumConstantsTests.SuccessfullyExtractsCStyleEnumConstants_Field.verified.txt index 5f282702bb..e652d4c57e 100644 --- a/tests/SilkTouch/SilkTouch/ExtractEnumConstantsTests.SuccessfullyExtractsCStyleEnumConstants_Field.verified.txt +++ b/tests/SilkTouch/SilkTouch/ExtractEnumConstantsTests.SuccessfullyExtractsCStyleEnumConstants_Field.verified.txt @@ -1 +1,23 @@ - \ No newline at end of file +// SDL_BlendMode.gen.cs +public enum SDL_BlendMode : uint +{ + SDL_BLENDMODE_NONE = 0x00000000U, + SDL_BLENDMODE_BLEND = 0x00000001U, + SDL_BLENDMODE_BLEND_PREMULTIPLIED = 0x00000010U, + SDL_BLENDMODE_ADD = 0x00000002U, + SDL_BLENDMODE_ADD_PREMULTIPLIED = 0x00000020U, + SDL_BLENDMODE_MOD = 0x00000004U, + SDL_BLENDMODE_MUL = 0x00000008U, + SDL_BLENDMODE_INVALID = 0x7FFFFFFFU +} + +// Sdl.gen.cs +public unsafe partial struct Sdl +{ +} + +public class Test +{ + [NativeTypeName("SDL_BlendMode")] + public SDL_BlendMode Blend; +} From e21b57636631ac4eaff6fc5cbeda10c410db5b72 Mon Sep 17 00:00:00 2001 From: William Chen Date: Tue, 9 Jun 2026 05:53:39 -0400 Subject: [PATCH 31/37] Generate on Windows --- sources/SDL/SDL/SDL3/ISdl.gen.cs | 4 ++-- sources/SDL/SDL/SDL3/Sdl.gen.cs | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/sources/SDL/SDL/SDL3/ISdl.gen.cs b/sources/SDL/SDL/SDL3/ISdl.gen.cs index d5c8c95c5f..608dfe1d1d 100644 --- a/sources/SDL/SDL/SDL3/ISdl.gen.cs +++ b/sources/SDL/SDL/SDL3/ISdl.gen.cs @@ -914,7 +914,7 @@ uint newval [NativeName("SDL_ComposeCustomBlendMode")] [NativeFunction("SDL3", EntryPoint = "SDL_ComposeCustomBlendMode")] - static abstract uint ComposeCustomBlendMode( + static abstract BlendMode ComposeCustomBlendMode( BlendFactor srcColorFactor, BlendFactor dstColorFactor, BlendOperation colorOperation, @@ -11555,7 +11555,7 @@ uint depth_or_layer_count [NativeName("SDL_ComposeCustomBlendMode")] [NativeFunction("SDL3", EntryPoint = "SDL_ComposeCustomBlendMode")] - uint ComposeCustomBlendMode( + BlendMode ComposeCustomBlendMode( BlendFactor srcColorFactor, BlendFactor dstColorFactor, BlendOperation colorOperation, diff --git a/sources/SDL/SDL/SDL3/Sdl.gen.cs b/sources/SDL/SDL/SDL3/Sdl.gen.cs index c4f2200c54..ca5c5513e2 100644 --- a/sources/SDL/SDL/SDL3/Sdl.gen.cs +++ b/sources/SDL/SDL/SDL3/Sdl.gen.cs @@ -1496,7 +1496,7 @@ uint newval [NativeName("SDL_ComposeCustomBlendMode")] [DllImport("SDL3", ExactSpelling = true, EntryPoint = "SDL_ComposeCustomBlendMode")] - public static extern uint ComposeCustomBlendMode( + public static extern BlendMode ComposeCustomBlendMode( BlendFactor srcColorFactor, BlendFactor dstColorFactor, BlendOperation colorOperation, @@ -18691,7 +18691,7 @@ uint newval [MethodImpl( MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization )] - public uint ComposeCustomBlendMode( + public BlendMode ComposeCustomBlendMode( BlendFactor srcColorFactor, BlendFactor dstColorFactor, BlendOperation colorOperation, @@ -36633,7 +36633,7 @@ uint newval [MethodImpl( MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization )] - public static uint ComposeCustomBlendMode( + public static BlendMode ComposeCustomBlendMode( BlendFactor srcColorFactor, BlendFactor dstColorFactor, BlendOperation colorOperation, @@ -63880,7 +63880,7 @@ uint newval [NativeName("SDL_ComposeCustomBlendMode")] [NativeFunction("SDL3", EntryPoint = "SDL_ComposeCustomBlendMode")] [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] - uint ISdl.ComposeCustomBlendMode( + BlendMode ISdl.ComposeCustomBlendMode( BlendFactor srcColorFactor, BlendFactor dstColorFactor, BlendOperation colorOperation, @@ -63896,7 +63896,7 @@ BlendOperation alphaOperation BlendFactor, BlendFactor, BlendOperation, - uint>)( + BlendMode>)( _slots[84] is not null and var loadedFnPtr ? loadedFnPtr : _slots[84] = nativeContext.LoadFunction("SDL_ComposeCustomBlendMode", "SDL3") @@ -63913,7 +63913,7 @@ _slots[84] is not null and var loadedFnPtr [NativeName("SDL_ComposeCustomBlendMode")] [NativeFunction("SDL3", EntryPoint = "SDL_ComposeCustomBlendMode")] [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] - public static uint ComposeCustomBlendMode( + public static BlendMode ComposeCustomBlendMode( BlendFactor srcColorFactor, BlendFactor dstColorFactor, BlendOperation colorOperation, From 29013a4aa36a8d9c9194fe602b28040049efa1f0 Mon Sep 17 00:00:00 2001 From: William Chen Date: Tue, 9 Jun 2026 05:55:52 -0400 Subject: [PATCH 32/37] Cleanup --- sources/SilkTouch/SilkTouch/Mods/ExtractEnumConstants.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/sources/SilkTouch/SilkTouch/Mods/ExtractEnumConstants.cs b/sources/SilkTouch/SilkTouch/Mods/ExtractEnumConstants.cs index e6734dfb5a..dd81c87c36 100644 --- a/sources/SilkTouch/SilkTouch/Mods/ExtractEnumConstants.cs +++ b/sources/SilkTouch/SilkTouch/Mods/ExtractEnumConstants.cs @@ -111,7 +111,6 @@ public async Task ExecuteAsync(IModContext ctx, CancellationToken ct = default) private static ReadOnlySpan GetNativeTypeNameForPredefinedType(PredefinedTypeSyntax node) { // Walk up to the parameter or method. We only allow primitive integer types right now. - SyntaxNode previous = node; var current = node.Parent; var indirectionLevels = 0; while (current is PointerTypeSyntax) From ce3c9c503117965055eef3c624c27d49e35009c6 Mon Sep 17 00:00:00 2001 From: William Chen Date: Tue, 9 Jun 2026 06:02:25 -0400 Subject: [PATCH 33/37] Annotate extracted enums with [NativeName] --- .../SilkTouch/Mods/ExtractEnumConstants.cs | 13 +++++++++---- ...lyExtractsCStyleEnumConstants_Field.verified.txt | 1 + ...CStyleEnumConstants_MethodParameter.verified.txt | 1 + ...ExtractsCStyleEnumConstants_Pointer.verified.txt | 1 + ...ractsCStyleEnumConstants_ReturnType.verified.txt | 1 + 5 files changed, 13 insertions(+), 4 deletions(-) diff --git a/sources/SilkTouch/SilkTouch/Mods/ExtractEnumConstants.cs b/sources/SilkTouch/SilkTouch/Mods/ExtractEnumConstants.cs index dd81c87c36..82c8034d52 100644 --- a/sources/SilkTouch/SilkTouch/Mods/ExtractEnumConstants.cs +++ b/sources/SilkTouch/SilkTouch/Mods/ExtractEnumConstants.cs @@ -352,10 +352,15 @@ var constCandidate in (ReadOnlySpan)[constant, trimmingName] break; } - theExtractedEnum.Node = theExtractedEnum.Node.AddMembers( - EnumMemberDeclaration(constant) - .WithEqualsValue(EqualsValueClause(value)) - ); + theExtractedEnum.Node = theExtractedEnum + .Node.AddMembers( + EnumMemberDeclaration(constant) + .WithEqualsValue(EqualsValueClause(value)) + ) + .WithAttributeLists( + theExtractedEnum.Node.AttributeLists.WithNativeName(enumName) + ); + extractedEnum = theExtractedEnum; break; } diff --git a/tests/SilkTouch/SilkTouch/ExtractEnumConstantsTests.SuccessfullyExtractsCStyleEnumConstants_Field.verified.txt b/tests/SilkTouch/SilkTouch/ExtractEnumConstantsTests.SuccessfullyExtractsCStyleEnumConstants_Field.verified.txt index e652d4c57e..bc67d17fc4 100644 --- a/tests/SilkTouch/SilkTouch/ExtractEnumConstantsTests.SuccessfullyExtractsCStyleEnumConstants_Field.verified.txt +++ b/tests/SilkTouch/SilkTouch/ExtractEnumConstantsTests.SuccessfullyExtractsCStyleEnumConstants_Field.verified.txt @@ -1,4 +1,5 @@ // SDL_BlendMode.gen.cs +[NativeName("SDL_BlendMode")] public enum SDL_BlendMode : uint { SDL_BLENDMODE_NONE = 0x00000000U, diff --git a/tests/SilkTouch/SilkTouch/ExtractEnumConstantsTests.SuccessfullyExtractsCStyleEnumConstants_MethodParameter.verified.txt b/tests/SilkTouch/SilkTouch/ExtractEnumConstantsTests.SuccessfullyExtractsCStyleEnumConstants_MethodParameter.verified.txt index 6381c82a87..b3ec17bbdb 100644 --- a/tests/SilkTouch/SilkTouch/ExtractEnumConstantsTests.SuccessfullyExtractsCStyleEnumConstants_MethodParameter.verified.txt +++ b/tests/SilkTouch/SilkTouch/ExtractEnumConstantsTests.SuccessfullyExtractsCStyleEnumConstants_MethodParameter.verified.txt @@ -1,4 +1,5 @@ // SDL_BlendMode.gen.cs +[NativeName("SDL_BlendMode")] public enum SDL_BlendMode : uint { SDL_BLENDMODE_NONE = 0x00000000U, diff --git a/tests/SilkTouch/SilkTouch/ExtractEnumConstantsTests.SuccessfullyExtractsCStyleEnumConstants_Pointer.verified.txt b/tests/SilkTouch/SilkTouch/ExtractEnumConstantsTests.SuccessfullyExtractsCStyleEnumConstants_Pointer.verified.txt index 3ff556a066..c28e8ef78d 100644 --- a/tests/SilkTouch/SilkTouch/ExtractEnumConstantsTests.SuccessfullyExtractsCStyleEnumConstants_Pointer.verified.txt +++ b/tests/SilkTouch/SilkTouch/ExtractEnumConstantsTests.SuccessfullyExtractsCStyleEnumConstants_Pointer.verified.txt @@ -1,4 +1,5 @@ // SDL_BlendMode.gen.cs +[NativeName("SDL_BlendMode")] public enum SDL_BlendMode : uint { SDL_BLENDMODE_NONE = 0x00000000U, diff --git a/tests/SilkTouch/SilkTouch/ExtractEnumConstantsTests.SuccessfullyExtractsCStyleEnumConstants_ReturnType.verified.txt b/tests/SilkTouch/SilkTouch/ExtractEnumConstantsTests.SuccessfullyExtractsCStyleEnumConstants_ReturnType.verified.txt index fcd4d92a22..95eb0696e8 100644 --- a/tests/SilkTouch/SilkTouch/ExtractEnumConstantsTests.SuccessfullyExtractsCStyleEnumConstants_ReturnType.verified.txt +++ b/tests/SilkTouch/SilkTouch/ExtractEnumConstantsTests.SuccessfullyExtractsCStyleEnumConstants_ReturnType.verified.txt @@ -1,4 +1,5 @@ // SDL_BlendMode.gen.cs +[NativeName("SDL_BlendMode")] public enum SDL_BlendMode : uint { SDL_BLENDMODE_NONE = 0x00000000U, From 1e3d68aa037b4e9fb94ce33013b6ebc67bccec7e Mon Sep 17 00:00:00 2001 From: William Chen Date: Tue, 9 Jun 2026 06:06:34 -0400 Subject: [PATCH 34/37] Generate on Windows --- sources/SDL/SDL/SDL3/BlendMode.gen.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/sources/SDL/SDL/SDL3/BlendMode.gen.cs b/sources/SDL/SDL/SDL3/BlendMode.gen.cs index 89924340d7..76f3f168ba 100644 --- a/sources/SDL/SDL/SDL3/BlendMode.gen.cs +++ b/sources/SDL/SDL/SDL3/BlendMode.gen.cs @@ -8,6 +8,7 @@ namespace Silk.NET.SDL; +[NativeName("SDL_BlendMode")] public enum BlendMode : uint { None = 0, From 98dadf98ac47eff8e65ea27f965fbdaae64388e5 Mon Sep 17 00:00:00 2001 From: William Chen Date: Tue, 9 Jun 2026 06:36:39 -0400 Subject: [PATCH 35/37] Add IEquatable to transformed handle types These already implemented all the required members for IEquatable, so I found it weird that they didn't also implement the interface. --- .../SilkTouch/SilkTouch/Mods/TransformHandles.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/sources/SilkTouch/SilkTouch/Mods/TransformHandles.cs b/sources/SilkTouch/SilkTouch/Mods/TransformHandles.cs index a337fa7c6c..e722c105a4 100644 --- a/sources/SilkTouch/SilkTouch/Mods/TransformHandles.cs +++ b/sources/SilkTouch/SilkTouch/Mods/TransformHandles.cs @@ -250,6 +250,22 @@ public override SyntaxNode VisitStructDeclaration(StructDeclarationSyntax node) Token(SyntaxKind.UnsafeKeyword), Token(SyntaxKind.PartialKeyword) ) + ) + .WithBaseList( + BaseList( + SingletonSeparatedList( + SimpleBaseType( + GenericName(Identifier("IEquatable")) + .WithTypeArgumentList( + TypeArgumentList( + SingletonSeparatedList( + IdentifierName(structName) + ) + ) + ) + ) + ) + ) ); } From 622a1f2772351d5edf9319763766ffd2230dfea1 Mon Sep 17 00:00:00 2001 From: William Chen Date: Tue, 9 Jun 2026 06:41:38 -0400 Subject: [PATCH 36/37] Generate on Windows for IEquatable handles change --- sources/OpenAL/OpenAL/Handles/ContextHandle.gen.cs | 2 +- sources/OpenAL/OpenAL/Handles/DeviceHandle.gen.cs | 2 +- sources/SDL/SDL/Handles/AudioStreamHandle.gen.cs | 2 +- sources/SDL/SDL/Handles/CameraHandle.gen.cs | 2 +- sources/SDL/SDL/Handles/ConditionHandle.gen.cs | 2 +- sources/SDL/SDL/Handles/CursorHandle.gen.cs | 2 +- sources/SDL/SDL/Handles/DisplayModeDataHandle.gen.cs | 2 +- sources/SDL/SDL/Handles/EnvironmentHandle.gen.cs | 2 +- sources/SDL/SDL/Handles/GLContextStateHandle.gen.cs | 2 +- sources/SDL/SDL/Handles/GamepadHandle.gen.cs | 2 +- sources/SDL/SDL/Handles/GpuBufferHandle.gen.cs | 2 +- sources/SDL/SDL/Handles/GpuCommandBufferHandle.gen.cs | 2 +- sources/SDL/SDL/Handles/GpuComputePassHandle.gen.cs | 2 +- sources/SDL/SDL/Handles/GpuComputePipelineHandle.gen.cs | 1 + sources/SDL/SDL/Handles/GpuCopyPassHandle.gen.cs | 2 +- sources/SDL/SDL/Handles/GpuDeviceHandle.gen.cs | 2 +- sources/SDL/SDL/Handles/GpuFenceHandle.gen.cs | 2 +- sources/SDL/SDL/Handles/GpuGraphicsPipelineHandle.gen.cs | 1 + sources/SDL/SDL/Handles/GpuRenderPassHandle.gen.cs | 2 +- sources/SDL/SDL/Handles/GpuRenderStateHandle.gen.cs | 2 +- sources/SDL/SDL/Handles/GpuSamplerHandle.gen.cs | 2 +- sources/SDL/SDL/Handles/GpuShaderHandle.gen.cs | 2 +- sources/SDL/SDL/Handles/GpuTextureHandle.gen.cs | 2 +- sources/SDL/SDL/Handles/GpuTransferBufferHandle.gen.cs | 2 +- sources/SDL/SDL/Handles/HapticHandle.gen.cs | 2 +- sources/SDL/SDL/Handles/HidDeviceHandle.gen.cs | 2 +- sources/SDL/SDL/Handles/IOStreamHandle.gen.cs | 2 +- sources/SDL/SDL/Handles/IconvDataTHandle.gen.cs | 2 +- sources/SDL/SDL/Handles/JoystickHandle.gen.cs | 2 +- sources/SDL/SDL/Handles/MutexHandle.gen.cs | 2 +- sources/SDL/SDL/Handles/ProcessHandle.gen.cs | 2 +- sources/SDL/SDL/Handles/RWLockHandle.gen.cs | 2 +- sources/SDL/SDL/Handles/RendererHandle.gen.cs | 2 +- sources/SDL/SDL/Handles/SemaphoreHandle.gen.cs | 2 +- sources/SDL/SDL/Handles/SensorHandle.gen.cs | 2 +- sources/SDL/SDL/Handles/SharedObjectHandle.gen.cs | 2 +- sources/SDL/SDL/Handles/StorageHandle.gen.cs | 2 +- sources/SDL/SDL/Handles/ThreadHandle.gen.cs | 2 +- sources/SDL/SDL/Handles/WindowHandle.gen.cs | 2 +- .../Vulkan/Vulkan/Handles/AccelerationStructureHandleKHR.gen.cs | 1 + .../Vulkan/Vulkan/Handles/AccelerationStructureHandleNV.gen.cs | 1 + sources/Vulkan/Vulkan/Handles/BufferHandle.gen.cs | 2 +- sources/Vulkan/Vulkan/Handles/BufferViewHandle.gen.cs | 2 +- sources/Vulkan/Vulkan/Handles/CommandBufferHandle.gen.cs | 2 +- sources/Vulkan/Vulkan/Handles/CommandPoolHandle.gen.cs | 2 +- sources/Vulkan/Vulkan/Handles/CuFunctionHandleNVX.gen.cs | 2 +- sources/Vulkan/Vulkan/Handles/CuModuleHandleNVX.gen.cs | 2 +- .../Vulkan/Handles/DataGraphPipelineSessionHandleARM.gen.cs | 1 + .../Vulkan/Vulkan/Handles/DebugReportCallbackHandleEXT.gen.cs | 1 + .../Vulkan/Vulkan/Handles/DebugUtilsMessengerHandleEXT.gen.cs | 1 + sources/Vulkan/Vulkan/Handles/DeferredOperationHandleKHR.gen.cs | 1 + sources/Vulkan/Vulkan/Handles/DescriptorPoolHandle.gen.cs | 2 +- sources/Vulkan/Vulkan/Handles/DescriptorSetHandle.gen.cs | 2 +- sources/Vulkan/Vulkan/Handles/DescriptorSetLayoutHandle.gen.cs | 1 + .../Vulkan/Vulkan/Handles/DescriptorUpdateTemplateHandle.gen.cs | 1 + sources/Vulkan/Vulkan/Handles/DeviceHandle.gen.cs | 2 +- sources/Vulkan/Vulkan/Handles/DeviceMemoryHandle.gen.cs | 2 +- sources/Vulkan/Vulkan/Handles/DisplayHandleKHR.gen.cs | 2 +- sources/Vulkan/Vulkan/Handles/DisplayModeHandleKHR.gen.cs | 2 +- sources/Vulkan/Vulkan/Handles/EventHandle.gen.cs | 2 +- .../Vulkan/Vulkan/Handles/ExternalComputeQueueHandleNV.gen.cs | 1 + sources/Vulkan/Vulkan/Handles/FenceHandle.gen.cs | 2 +- sources/Vulkan/Vulkan/Handles/FramebufferHandle.gen.cs | 2 +- sources/Vulkan/Vulkan/Handles/ImageHandle.gen.cs | 2 +- sources/Vulkan/Vulkan/Handles/ImageViewHandle.gen.cs | 2 +- .../Vulkan/Handles/IndirectCommandsLayoutHandleEXT.gen.cs | 1 + .../Vulkan/Vulkan/Handles/IndirectCommandsLayoutHandleNV.gen.cs | 1 + .../Vulkan/Vulkan/Handles/IndirectExecutionSetHandleEXT.gen.cs | 1 + sources/Vulkan/Vulkan/Handles/InstanceHandle.gen.cs | 2 +- sources/Vulkan/Vulkan/Handles/MicromapHandleEXT.gen.cs | 2 +- sources/Vulkan/Vulkan/Handles/OpticalFlowSessionHandleNV.gen.cs | 1 + .../Vulkan/Handles/PerformanceConfigurationHandleINTEL.gen.cs | 1 + sources/Vulkan/Vulkan/Handles/PhysicalDeviceHandle.gen.cs | 2 +- sources/Vulkan/Vulkan/Handles/PipelineBinaryHandleKHR.gen.cs | 2 +- sources/Vulkan/Vulkan/Handles/PipelineCacheHandle.gen.cs | 2 +- sources/Vulkan/Vulkan/Handles/PipelineHandle.gen.cs | 2 +- sources/Vulkan/Vulkan/Handles/PipelineLayoutHandle.gen.cs | 2 +- sources/Vulkan/Vulkan/Handles/PrivateDataSlotHandle.gen.cs | 2 +- sources/Vulkan/Vulkan/Handles/QueryPoolHandle.gen.cs | 2 +- sources/Vulkan/Vulkan/Handles/QueueHandle.gen.cs | 2 +- sources/Vulkan/Vulkan/Handles/RenderPassHandle.gen.cs | 2 +- sources/Vulkan/Vulkan/Handles/SamplerHandle.gen.cs | 2 +- .../Vulkan/Vulkan/Handles/SamplerYcbcrConversionHandle.gen.cs | 1 + sources/Vulkan/Vulkan/Handles/SemaphoreHandle.gen.cs | 2 +- sources/Vulkan/Vulkan/Handles/ShaderHandleEXT.gen.cs | 2 +- sources/Vulkan/Vulkan/Handles/ShaderModuleHandle.gen.cs | 2 +- sources/Vulkan/Vulkan/Handles/SurfaceHandleKHR.gen.cs | 2 +- sources/Vulkan/Vulkan/Handles/SwapchainHandleKHR.gen.cs | 2 +- sources/Vulkan/Vulkan/Handles/TensorHandleARM.gen.cs | 2 +- sources/Vulkan/Vulkan/Handles/TensorViewHandleARM.gen.cs | 2 +- sources/Vulkan/Vulkan/Handles/ValidationCacheHandleEXT.gen.cs | 1 + sources/Vulkan/Vulkan/Handles/VideoSessionHandleKHR.gen.cs | 2 +- .../Vulkan/Handles/VideoSessionParametersHandleKHR.gen.cs | 1 + 93 files changed, 93 insertions(+), 74 deletions(-) diff --git a/sources/OpenAL/OpenAL/Handles/ContextHandle.gen.cs b/sources/OpenAL/OpenAL/Handles/ContextHandle.gen.cs index 5c15c4fb93..9dbc1cb8c4 100644 --- a/sources/OpenAL/OpenAL/Handles/ContextHandle.gen.cs +++ b/sources/OpenAL/OpenAL/Handles/ContextHandle.gen.cs @@ -11,7 +11,7 @@ namespace Silk.NET.OpenAL; [NativeName("ALCcontext")] -public readonly unsafe partial struct ContextHandle +public readonly unsafe partial struct ContextHandle : IEquatable { public readonly void* Handle; diff --git a/sources/OpenAL/OpenAL/Handles/DeviceHandle.gen.cs b/sources/OpenAL/OpenAL/Handles/DeviceHandle.gen.cs index 3142e2084a..240556c635 100644 --- a/sources/OpenAL/OpenAL/Handles/DeviceHandle.gen.cs +++ b/sources/OpenAL/OpenAL/Handles/DeviceHandle.gen.cs @@ -11,7 +11,7 @@ namespace Silk.NET.OpenAL; [NativeName("ALCdevice")] -public readonly unsafe partial struct DeviceHandle +public readonly unsafe partial struct DeviceHandle : IEquatable { public readonly void* Handle; diff --git a/sources/SDL/SDL/Handles/AudioStreamHandle.gen.cs b/sources/SDL/SDL/Handles/AudioStreamHandle.gen.cs index 7a7b89eb90..13bdad885b 100644 --- a/sources/SDL/SDL/Handles/AudioStreamHandle.gen.cs +++ b/sources/SDL/SDL/Handles/AudioStreamHandle.gen.cs @@ -9,7 +9,7 @@ namespace Silk.NET.SDL; [NativeName("SDL_AudioStream")] -public readonly unsafe partial struct AudioStreamHandle +public readonly unsafe partial struct AudioStreamHandle : IEquatable { public readonly void* Handle; diff --git a/sources/SDL/SDL/Handles/CameraHandle.gen.cs b/sources/SDL/SDL/Handles/CameraHandle.gen.cs index 3bca5b66b6..c0d29f9813 100644 --- a/sources/SDL/SDL/Handles/CameraHandle.gen.cs +++ b/sources/SDL/SDL/Handles/CameraHandle.gen.cs @@ -9,7 +9,7 @@ namespace Silk.NET.SDL; [NativeName("SDL_Camera")] -public readonly unsafe partial struct CameraHandle +public readonly unsafe partial struct CameraHandle : IEquatable { public readonly void* Handle; diff --git a/sources/SDL/SDL/Handles/ConditionHandle.gen.cs b/sources/SDL/SDL/Handles/ConditionHandle.gen.cs index 47f3c3ab8a..ba35465896 100644 --- a/sources/SDL/SDL/Handles/ConditionHandle.gen.cs +++ b/sources/SDL/SDL/Handles/ConditionHandle.gen.cs @@ -9,7 +9,7 @@ namespace Silk.NET.SDL; [NativeName("SDL_Condition")] -public readonly unsafe partial struct ConditionHandle +public readonly unsafe partial struct ConditionHandle : IEquatable { public readonly void* Handle; diff --git a/sources/SDL/SDL/Handles/CursorHandle.gen.cs b/sources/SDL/SDL/Handles/CursorHandle.gen.cs index 1ccd8ca8af..eec52dad40 100644 --- a/sources/SDL/SDL/Handles/CursorHandle.gen.cs +++ b/sources/SDL/SDL/Handles/CursorHandle.gen.cs @@ -9,7 +9,7 @@ namespace Silk.NET.SDL; [NativeName("SDL_Cursor")] -public readonly unsafe partial struct CursorHandle +public readonly unsafe partial struct CursorHandle : IEquatable { public readonly void* Handle; diff --git a/sources/SDL/SDL/Handles/DisplayModeDataHandle.gen.cs b/sources/SDL/SDL/Handles/DisplayModeDataHandle.gen.cs index 7abf2c85d1..9fbeb3149d 100644 --- a/sources/SDL/SDL/Handles/DisplayModeDataHandle.gen.cs +++ b/sources/SDL/SDL/Handles/DisplayModeDataHandle.gen.cs @@ -9,7 +9,7 @@ namespace Silk.NET.SDL; [NativeName("SDL_DisplayModeData")] -public readonly unsafe partial struct DisplayModeDataHandle +public readonly unsafe partial struct DisplayModeDataHandle : IEquatable { public readonly void* Handle; diff --git a/sources/SDL/SDL/Handles/EnvironmentHandle.gen.cs b/sources/SDL/SDL/Handles/EnvironmentHandle.gen.cs index 8e8591a674..c27314fb8a 100644 --- a/sources/SDL/SDL/Handles/EnvironmentHandle.gen.cs +++ b/sources/SDL/SDL/Handles/EnvironmentHandle.gen.cs @@ -9,7 +9,7 @@ namespace Silk.NET.SDL; [NativeName("SDL_Environment")] -public readonly unsafe partial struct EnvironmentHandle +public readonly unsafe partial struct EnvironmentHandle : IEquatable { public readonly void* Handle; diff --git a/sources/SDL/SDL/Handles/GLContextStateHandle.gen.cs b/sources/SDL/SDL/Handles/GLContextStateHandle.gen.cs index f1e92e496b..fff5b39c08 100644 --- a/sources/SDL/SDL/Handles/GLContextStateHandle.gen.cs +++ b/sources/SDL/SDL/Handles/GLContextStateHandle.gen.cs @@ -9,7 +9,7 @@ namespace Silk.NET.SDL; [NativeName("SDL_GLContextState")] -public readonly unsafe partial struct GLContextStateHandle +public readonly unsafe partial struct GLContextStateHandle : IEquatable { public readonly void* Handle; diff --git a/sources/SDL/SDL/Handles/GamepadHandle.gen.cs b/sources/SDL/SDL/Handles/GamepadHandle.gen.cs index 211fb22364..dae6f276d7 100644 --- a/sources/SDL/SDL/Handles/GamepadHandle.gen.cs +++ b/sources/SDL/SDL/Handles/GamepadHandle.gen.cs @@ -9,7 +9,7 @@ namespace Silk.NET.SDL; [NativeName("SDL_Gamepad")] -public readonly unsafe partial struct GamepadHandle +public readonly unsafe partial struct GamepadHandle : IEquatable { public readonly void* Handle; diff --git a/sources/SDL/SDL/Handles/GpuBufferHandle.gen.cs b/sources/SDL/SDL/Handles/GpuBufferHandle.gen.cs index 441a6b5c1c..fcc5c2ff78 100644 --- a/sources/SDL/SDL/Handles/GpuBufferHandle.gen.cs +++ b/sources/SDL/SDL/Handles/GpuBufferHandle.gen.cs @@ -9,7 +9,7 @@ namespace Silk.NET.SDL; [NativeName("SDL_GPUBuffer")] -public readonly unsafe partial struct GpuBufferHandle +public readonly unsafe partial struct GpuBufferHandle : IEquatable { public readonly void* Handle; diff --git a/sources/SDL/SDL/Handles/GpuCommandBufferHandle.gen.cs b/sources/SDL/SDL/Handles/GpuCommandBufferHandle.gen.cs index 127bfc2288..362afda340 100644 --- a/sources/SDL/SDL/Handles/GpuCommandBufferHandle.gen.cs +++ b/sources/SDL/SDL/Handles/GpuCommandBufferHandle.gen.cs @@ -9,7 +9,7 @@ namespace Silk.NET.SDL; [NativeName("SDL_GPUCommandBuffer")] -public readonly unsafe partial struct GpuCommandBufferHandle +public readonly unsafe partial struct GpuCommandBufferHandle : IEquatable { public readonly void* Handle; diff --git a/sources/SDL/SDL/Handles/GpuComputePassHandle.gen.cs b/sources/SDL/SDL/Handles/GpuComputePassHandle.gen.cs index b8d8dcdc03..c7df4c054d 100644 --- a/sources/SDL/SDL/Handles/GpuComputePassHandle.gen.cs +++ b/sources/SDL/SDL/Handles/GpuComputePassHandle.gen.cs @@ -9,7 +9,7 @@ namespace Silk.NET.SDL; [NativeName("SDL_GPUComputePass")] -public readonly unsafe partial struct GpuComputePassHandle +public readonly unsafe partial struct GpuComputePassHandle : IEquatable { public readonly void* Handle; diff --git a/sources/SDL/SDL/Handles/GpuComputePipelineHandle.gen.cs b/sources/SDL/SDL/Handles/GpuComputePipelineHandle.gen.cs index 15d5e9fcc3..4034d392e8 100644 --- a/sources/SDL/SDL/Handles/GpuComputePipelineHandle.gen.cs +++ b/sources/SDL/SDL/Handles/GpuComputePipelineHandle.gen.cs @@ -10,6 +10,7 @@ namespace Silk.NET.SDL; [NativeName("SDL_GPUComputePipeline")] public readonly unsafe partial struct GpuComputePipelineHandle + : IEquatable { public readonly void* Handle; diff --git a/sources/SDL/SDL/Handles/GpuCopyPassHandle.gen.cs b/sources/SDL/SDL/Handles/GpuCopyPassHandle.gen.cs index a4395e1e96..f7eaffb9c2 100644 --- a/sources/SDL/SDL/Handles/GpuCopyPassHandle.gen.cs +++ b/sources/SDL/SDL/Handles/GpuCopyPassHandle.gen.cs @@ -9,7 +9,7 @@ namespace Silk.NET.SDL; [NativeName("SDL_GPUCopyPass")] -public readonly unsafe partial struct GpuCopyPassHandle +public readonly unsafe partial struct GpuCopyPassHandle : IEquatable { public readonly void* Handle; diff --git a/sources/SDL/SDL/Handles/GpuDeviceHandle.gen.cs b/sources/SDL/SDL/Handles/GpuDeviceHandle.gen.cs index 2934694a99..40918cdd2c 100644 --- a/sources/SDL/SDL/Handles/GpuDeviceHandle.gen.cs +++ b/sources/SDL/SDL/Handles/GpuDeviceHandle.gen.cs @@ -9,7 +9,7 @@ namespace Silk.NET.SDL; [NativeName("SDL_GPUDevice")] -public readonly unsafe partial struct GpuDeviceHandle +public readonly unsafe partial struct GpuDeviceHandle : IEquatable { public readonly void* Handle; diff --git a/sources/SDL/SDL/Handles/GpuFenceHandle.gen.cs b/sources/SDL/SDL/Handles/GpuFenceHandle.gen.cs index c273ebc876..f93d869a8c 100644 --- a/sources/SDL/SDL/Handles/GpuFenceHandle.gen.cs +++ b/sources/SDL/SDL/Handles/GpuFenceHandle.gen.cs @@ -9,7 +9,7 @@ namespace Silk.NET.SDL; [NativeName("SDL_GPUFence")] -public readonly unsafe partial struct GpuFenceHandle +public readonly unsafe partial struct GpuFenceHandle : IEquatable { public readonly void* Handle; diff --git a/sources/SDL/SDL/Handles/GpuGraphicsPipelineHandle.gen.cs b/sources/SDL/SDL/Handles/GpuGraphicsPipelineHandle.gen.cs index 1f9636a547..b2930c73e2 100644 --- a/sources/SDL/SDL/Handles/GpuGraphicsPipelineHandle.gen.cs +++ b/sources/SDL/SDL/Handles/GpuGraphicsPipelineHandle.gen.cs @@ -10,6 +10,7 @@ namespace Silk.NET.SDL; [NativeName("SDL_GPUGraphicsPipeline")] public readonly unsafe partial struct GpuGraphicsPipelineHandle + : IEquatable { public readonly void* Handle; diff --git a/sources/SDL/SDL/Handles/GpuRenderPassHandle.gen.cs b/sources/SDL/SDL/Handles/GpuRenderPassHandle.gen.cs index bd817ca4c5..50e069d7aa 100644 --- a/sources/SDL/SDL/Handles/GpuRenderPassHandle.gen.cs +++ b/sources/SDL/SDL/Handles/GpuRenderPassHandle.gen.cs @@ -9,7 +9,7 @@ namespace Silk.NET.SDL; [NativeName("SDL_GPURenderPass")] -public readonly unsafe partial struct GpuRenderPassHandle +public readonly unsafe partial struct GpuRenderPassHandle : IEquatable { public readonly void* Handle; diff --git a/sources/SDL/SDL/Handles/GpuRenderStateHandle.gen.cs b/sources/SDL/SDL/Handles/GpuRenderStateHandle.gen.cs index bf90afce7c..14812e7050 100644 --- a/sources/SDL/SDL/Handles/GpuRenderStateHandle.gen.cs +++ b/sources/SDL/SDL/Handles/GpuRenderStateHandle.gen.cs @@ -9,7 +9,7 @@ namespace Silk.NET.SDL; [NativeName("SDL_GPURenderState")] -public readonly unsafe partial struct GpuRenderStateHandle +public readonly unsafe partial struct GpuRenderStateHandle : IEquatable { public readonly void* Handle; diff --git a/sources/SDL/SDL/Handles/GpuSamplerHandle.gen.cs b/sources/SDL/SDL/Handles/GpuSamplerHandle.gen.cs index 4964892f5d..9c50c7fee2 100644 --- a/sources/SDL/SDL/Handles/GpuSamplerHandle.gen.cs +++ b/sources/SDL/SDL/Handles/GpuSamplerHandle.gen.cs @@ -9,7 +9,7 @@ namespace Silk.NET.SDL; [NativeName("SDL_GPUSampler")] -public readonly unsafe partial struct GpuSamplerHandle +public readonly unsafe partial struct GpuSamplerHandle : IEquatable { public readonly void* Handle; diff --git a/sources/SDL/SDL/Handles/GpuShaderHandle.gen.cs b/sources/SDL/SDL/Handles/GpuShaderHandle.gen.cs index c5c1af3178..d091896d89 100644 --- a/sources/SDL/SDL/Handles/GpuShaderHandle.gen.cs +++ b/sources/SDL/SDL/Handles/GpuShaderHandle.gen.cs @@ -9,7 +9,7 @@ namespace Silk.NET.SDL; [NativeName("SDL_GPUShader")] -public readonly unsafe partial struct GpuShaderHandle +public readonly unsafe partial struct GpuShaderHandle : IEquatable { public readonly void* Handle; diff --git a/sources/SDL/SDL/Handles/GpuTextureHandle.gen.cs b/sources/SDL/SDL/Handles/GpuTextureHandle.gen.cs index 1f4e485615..307afa9d89 100644 --- a/sources/SDL/SDL/Handles/GpuTextureHandle.gen.cs +++ b/sources/SDL/SDL/Handles/GpuTextureHandle.gen.cs @@ -9,7 +9,7 @@ namespace Silk.NET.SDL; [NativeName("SDL_GPUTexture")] -public readonly unsafe partial struct GpuTextureHandle +public readonly unsafe partial struct GpuTextureHandle : IEquatable { public readonly void* Handle; diff --git a/sources/SDL/SDL/Handles/GpuTransferBufferHandle.gen.cs b/sources/SDL/SDL/Handles/GpuTransferBufferHandle.gen.cs index 23d2a08016..3b66ad704e 100644 --- a/sources/SDL/SDL/Handles/GpuTransferBufferHandle.gen.cs +++ b/sources/SDL/SDL/Handles/GpuTransferBufferHandle.gen.cs @@ -9,7 +9,7 @@ namespace Silk.NET.SDL; [NativeName("SDL_GPUTransferBuffer")] -public readonly unsafe partial struct GpuTransferBufferHandle +public readonly unsafe partial struct GpuTransferBufferHandle : IEquatable { public readonly void* Handle; diff --git a/sources/SDL/SDL/Handles/HapticHandle.gen.cs b/sources/SDL/SDL/Handles/HapticHandle.gen.cs index 2482783395..75285352bb 100644 --- a/sources/SDL/SDL/Handles/HapticHandle.gen.cs +++ b/sources/SDL/SDL/Handles/HapticHandle.gen.cs @@ -9,7 +9,7 @@ namespace Silk.NET.SDL; [NativeName("SDL_Haptic")] -public readonly unsafe partial struct HapticHandle +public readonly unsafe partial struct HapticHandle : IEquatable { public readonly void* Handle; diff --git a/sources/SDL/SDL/Handles/HidDeviceHandle.gen.cs b/sources/SDL/SDL/Handles/HidDeviceHandle.gen.cs index ad9360bd75..a14f7a2c49 100644 --- a/sources/SDL/SDL/Handles/HidDeviceHandle.gen.cs +++ b/sources/SDL/SDL/Handles/HidDeviceHandle.gen.cs @@ -9,7 +9,7 @@ namespace Silk.NET.SDL; [NativeName("SDL_hid_device")] -public readonly unsafe partial struct HidDeviceHandle +public readonly unsafe partial struct HidDeviceHandle : IEquatable { public readonly void* Handle; diff --git a/sources/SDL/SDL/Handles/IOStreamHandle.gen.cs b/sources/SDL/SDL/Handles/IOStreamHandle.gen.cs index b12053e4c2..a941ade657 100644 --- a/sources/SDL/SDL/Handles/IOStreamHandle.gen.cs +++ b/sources/SDL/SDL/Handles/IOStreamHandle.gen.cs @@ -9,7 +9,7 @@ namespace Silk.NET.SDL; [NativeName("SDL_IOStream")] -public readonly unsafe partial struct IOStreamHandle +public readonly unsafe partial struct IOStreamHandle : IEquatable { public readonly void* Handle; diff --git a/sources/SDL/SDL/Handles/IconvDataTHandle.gen.cs b/sources/SDL/SDL/Handles/IconvDataTHandle.gen.cs index 7ac28b98a0..b8b7898a59 100644 --- a/sources/SDL/SDL/Handles/IconvDataTHandle.gen.cs +++ b/sources/SDL/SDL/Handles/IconvDataTHandle.gen.cs @@ -9,7 +9,7 @@ namespace Silk.NET.SDL; [NativeName("SDL_iconv_data_t")] -public readonly unsafe partial struct IconvDataTHandle +public readonly unsafe partial struct IconvDataTHandle : IEquatable { public readonly void* Handle; diff --git a/sources/SDL/SDL/Handles/JoystickHandle.gen.cs b/sources/SDL/SDL/Handles/JoystickHandle.gen.cs index 4e7276dd3e..44e0e96622 100644 --- a/sources/SDL/SDL/Handles/JoystickHandle.gen.cs +++ b/sources/SDL/SDL/Handles/JoystickHandle.gen.cs @@ -9,7 +9,7 @@ namespace Silk.NET.SDL; [NativeName("SDL_Joystick")] -public readonly unsafe partial struct JoystickHandle +public readonly unsafe partial struct JoystickHandle : IEquatable { public readonly void* Handle; diff --git a/sources/SDL/SDL/Handles/MutexHandle.gen.cs b/sources/SDL/SDL/Handles/MutexHandle.gen.cs index c51b3cb34a..1db5ac5920 100644 --- a/sources/SDL/SDL/Handles/MutexHandle.gen.cs +++ b/sources/SDL/SDL/Handles/MutexHandle.gen.cs @@ -9,7 +9,7 @@ namespace Silk.NET.SDL; [NativeName("SDL_Mutex")] -public readonly unsafe partial struct MutexHandle +public readonly unsafe partial struct MutexHandle : IEquatable { public readonly void* Handle; diff --git a/sources/SDL/SDL/Handles/ProcessHandle.gen.cs b/sources/SDL/SDL/Handles/ProcessHandle.gen.cs index 95ec428a48..b8ffa75dbf 100644 --- a/sources/SDL/SDL/Handles/ProcessHandle.gen.cs +++ b/sources/SDL/SDL/Handles/ProcessHandle.gen.cs @@ -9,7 +9,7 @@ namespace Silk.NET.SDL; [NativeName("SDL_Process")] -public readonly unsafe partial struct ProcessHandle +public readonly unsafe partial struct ProcessHandle : IEquatable { public readonly void* Handle; diff --git a/sources/SDL/SDL/Handles/RWLockHandle.gen.cs b/sources/SDL/SDL/Handles/RWLockHandle.gen.cs index dd9700c19f..955896e679 100644 --- a/sources/SDL/SDL/Handles/RWLockHandle.gen.cs +++ b/sources/SDL/SDL/Handles/RWLockHandle.gen.cs @@ -9,7 +9,7 @@ namespace Silk.NET.SDL; [NativeName("SDL_RWLock")] -public readonly unsafe partial struct RWLockHandle +public readonly unsafe partial struct RWLockHandle : IEquatable { public readonly void* Handle; diff --git a/sources/SDL/SDL/Handles/RendererHandle.gen.cs b/sources/SDL/SDL/Handles/RendererHandle.gen.cs index 053e82e57e..d8c35d0a6e 100644 --- a/sources/SDL/SDL/Handles/RendererHandle.gen.cs +++ b/sources/SDL/SDL/Handles/RendererHandle.gen.cs @@ -9,7 +9,7 @@ namespace Silk.NET.SDL; [NativeName("SDL_Renderer")] -public readonly unsafe partial struct RendererHandle +public readonly unsafe partial struct RendererHandle : IEquatable { public readonly void* Handle; diff --git a/sources/SDL/SDL/Handles/SemaphoreHandle.gen.cs b/sources/SDL/SDL/Handles/SemaphoreHandle.gen.cs index c99c29686e..11a5d9129a 100644 --- a/sources/SDL/SDL/Handles/SemaphoreHandle.gen.cs +++ b/sources/SDL/SDL/Handles/SemaphoreHandle.gen.cs @@ -9,7 +9,7 @@ namespace Silk.NET.SDL; [NativeName("SDL_Semaphore")] -public readonly unsafe partial struct SemaphoreHandle +public readonly unsafe partial struct SemaphoreHandle : IEquatable { public readonly void* Handle; diff --git a/sources/SDL/SDL/Handles/SensorHandle.gen.cs b/sources/SDL/SDL/Handles/SensorHandle.gen.cs index 4820b8d143..65ac93fd60 100644 --- a/sources/SDL/SDL/Handles/SensorHandle.gen.cs +++ b/sources/SDL/SDL/Handles/SensorHandle.gen.cs @@ -9,7 +9,7 @@ namespace Silk.NET.SDL; [NativeName("SDL_Sensor")] -public readonly unsafe partial struct SensorHandle +public readonly unsafe partial struct SensorHandle : IEquatable { public readonly void* Handle; diff --git a/sources/SDL/SDL/Handles/SharedObjectHandle.gen.cs b/sources/SDL/SDL/Handles/SharedObjectHandle.gen.cs index c594be4b4c..f9c0dab805 100644 --- a/sources/SDL/SDL/Handles/SharedObjectHandle.gen.cs +++ b/sources/SDL/SDL/Handles/SharedObjectHandle.gen.cs @@ -9,7 +9,7 @@ namespace Silk.NET.SDL; [NativeName("SDL_SharedObject")] -public readonly unsafe partial struct SharedObjectHandle +public readonly unsafe partial struct SharedObjectHandle : IEquatable { public readonly void* Handle; diff --git a/sources/SDL/SDL/Handles/StorageHandle.gen.cs b/sources/SDL/SDL/Handles/StorageHandle.gen.cs index 1f7209c7ad..1b043d2585 100644 --- a/sources/SDL/SDL/Handles/StorageHandle.gen.cs +++ b/sources/SDL/SDL/Handles/StorageHandle.gen.cs @@ -9,7 +9,7 @@ namespace Silk.NET.SDL; [NativeName("SDL_Storage")] -public readonly unsafe partial struct StorageHandle +public readonly unsafe partial struct StorageHandle : IEquatable { public readonly void* Handle; diff --git a/sources/SDL/SDL/Handles/ThreadHandle.gen.cs b/sources/SDL/SDL/Handles/ThreadHandle.gen.cs index e87920c916..d2d1935800 100644 --- a/sources/SDL/SDL/Handles/ThreadHandle.gen.cs +++ b/sources/SDL/SDL/Handles/ThreadHandle.gen.cs @@ -9,7 +9,7 @@ namespace Silk.NET.SDL; [NativeName("SDL_Thread")] -public readonly unsafe partial struct ThreadHandle +public readonly unsafe partial struct ThreadHandle : IEquatable { public readonly void* Handle; diff --git a/sources/SDL/SDL/Handles/WindowHandle.gen.cs b/sources/SDL/SDL/Handles/WindowHandle.gen.cs index 597ca13bd8..96434fcd75 100644 --- a/sources/SDL/SDL/Handles/WindowHandle.gen.cs +++ b/sources/SDL/SDL/Handles/WindowHandle.gen.cs @@ -9,7 +9,7 @@ namespace Silk.NET.SDL; [NativeName("SDL_Window")] -public readonly unsafe partial struct WindowHandle +public readonly unsafe partial struct WindowHandle : IEquatable { public readonly void* Handle; diff --git a/sources/Vulkan/Vulkan/Handles/AccelerationStructureHandleKHR.gen.cs b/sources/Vulkan/Vulkan/Handles/AccelerationStructureHandleKHR.gen.cs index 5ce625e52e..76fe391cbb 100644 --- a/sources/Vulkan/Vulkan/Handles/AccelerationStructureHandleKHR.gen.cs +++ b/sources/Vulkan/Vulkan/Handles/AccelerationStructureHandleKHR.gen.cs @@ -10,6 +10,7 @@ namespace Silk.NET.Vulkan; [NativeName("VkAccelerationStructureKHR")] public readonly unsafe partial struct AccelerationStructureHandleKHR + : IEquatable { public readonly void* Handle; diff --git a/sources/Vulkan/Vulkan/Handles/AccelerationStructureHandleNV.gen.cs b/sources/Vulkan/Vulkan/Handles/AccelerationStructureHandleNV.gen.cs index e0b5cfd31a..9a06651042 100644 --- a/sources/Vulkan/Vulkan/Handles/AccelerationStructureHandleNV.gen.cs +++ b/sources/Vulkan/Vulkan/Handles/AccelerationStructureHandleNV.gen.cs @@ -10,6 +10,7 @@ namespace Silk.NET.Vulkan; [NativeName("VkAccelerationStructureNV")] public readonly unsafe partial struct AccelerationStructureHandleNV + : IEquatable { public readonly void* Handle; diff --git a/sources/Vulkan/Vulkan/Handles/BufferHandle.gen.cs b/sources/Vulkan/Vulkan/Handles/BufferHandle.gen.cs index ee402effc7..279ce57be4 100644 --- a/sources/Vulkan/Vulkan/Handles/BufferHandle.gen.cs +++ b/sources/Vulkan/Vulkan/Handles/BufferHandle.gen.cs @@ -9,7 +9,7 @@ namespace Silk.NET.Vulkan; [NativeName("VkBuffer")] -public readonly unsafe partial struct BufferHandle +public readonly unsafe partial struct BufferHandle : IEquatable { public readonly void* Handle; diff --git a/sources/Vulkan/Vulkan/Handles/BufferViewHandle.gen.cs b/sources/Vulkan/Vulkan/Handles/BufferViewHandle.gen.cs index fbd8cf601d..300221eaef 100644 --- a/sources/Vulkan/Vulkan/Handles/BufferViewHandle.gen.cs +++ b/sources/Vulkan/Vulkan/Handles/BufferViewHandle.gen.cs @@ -9,7 +9,7 @@ namespace Silk.NET.Vulkan; [NativeName("VkBufferView")] -public readonly unsafe partial struct BufferViewHandle +public readonly unsafe partial struct BufferViewHandle : IEquatable { public readonly void* Handle; diff --git a/sources/Vulkan/Vulkan/Handles/CommandBufferHandle.gen.cs b/sources/Vulkan/Vulkan/Handles/CommandBufferHandle.gen.cs index 78040914cc..19140194de 100644 --- a/sources/Vulkan/Vulkan/Handles/CommandBufferHandle.gen.cs +++ b/sources/Vulkan/Vulkan/Handles/CommandBufferHandle.gen.cs @@ -9,7 +9,7 @@ namespace Silk.NET.Vulkan; [NativeName("VkCommandBuffer")] -public readonly unsafe partial struct CommandBufferHandle +public readonly unsafe partial struct CommandBufferHandle : IEquatable { public readonly void* Handle; diff --git a/sources/Vulkan/Vulkan/Handles/CommandPoolHandle.gen.cs b/sources/Vulkan/Vulkan/Handles/CommandPoolHandle.gen.cs index 8901079ffa..41754f71c5 100644 --- a/sources/Vulkan/Vulkan/Handles/CommandPoolHandle.gen.cs +++ b/sources/Vulkan/Vulkan/Handles/CommandPoolHandle.gen.cs @@ -9,7 +9,7 @@ namespace Silk.NET.Vulkan; [NativeName("VkCommandPool")] -public readonly unsafe partial struct CommandPoolHandle +public readonly unsafe partial struct CommandPoolHandle : IEquatable { public readonly void* Handle; diff --git a/sources/Vulkan/Vulkan/Handles/CuFunctionHandleNVX.gen.cs b/sources/Vulkan/Vulkan/Handles/CuFunctionHandleNVX.gen.cs index 2092efbac6..88e3245aa6 100644 --- a/sources/Vulkan/Vulkan/Handles/CuFunctionHandleNVX.gen.cs +++ b/sources/Vulkan/Vulkan/Handles/CuFunctionHandleNVX.gen.cs @@ -9,7 +9,7 @@ namespace Silk.NET.Vulkan; [NativeName("VkCuFunctionNVX")] -public readonly unsafe partial struct CuFunctionHandleNVX +public readonly unsafe partial struct CuFunctionHandleNVX : IEquatable { public readonly void* Handle; diff --git a/sources/Vulkan/Vulkan/Handles/CuModuleHandleNVX.gen.cs b/sources/Vulkan/Vulkan/Handles/CuModuleHandleNVX.gen.cs index 38b157fb12..0c458ddb9a 100644 --- a/sources/Vulkan/Vulkan/Handles/CuModuleHandleNVX.gen.cs +++ b/sources/Vulkan/Vulkan/Handles/CuModuleHandleNVX.gen.cs @@ -9,7 +9,7 @@ namespace Silk.NET.Vulkan; [NativeName("VkCuModuleNVX")] -public readonly unsafe partial struct CuModuleHandleNVX +public readonly unsafe partial struct CuModuleHandleNVX : IEquatable { public readonly void* Handle; diff --git a/sources/Vulkan/Vulkan/Handles/DataGraphPipelineSessionHandleARM.gen.cs b/sources/Vulkan/Vulkan/Handles/DataGraphPipelineSessionHandleARM.gen.cs index 9fc3cce025..c168c7c13f 100644 --- a/sources/Vulkan/Vulkan/Handles/DataGraphPipelineSessionHandleARM.gen.cs +++ b/sources/Vulkan/Vulkan/Handles/DataGraphPipelineSessionHandleARM.gen.cs @@ -10,6 +10,7 @@ namespace Silk.NET.Vulkan; [NativeName("VkDataGraphPipelineSessionARM")] public readonly unsafe partial struct DataGraphPipelineSessionHandleARM + : IEquatable { public readonly void* Handle; diff --git a/sources/Vulkan/Vulkan/Handles/DebugReportCallbackHandleEXT.gen.cs b/sources/Vulkan/Vulkan/Handles/DebugReportCallbackHandleEXT.gen.cs index 9b5e6d4ba1..48fe437077 100644 --- a/sources/Vulkan/Vulkan/Handles/DebugReportCallbackHandleEXT.gen.cs +++ b/sources/Vulkan/Vulkan/Handles/DebugReportCallbackHandleEXT.gen.cs @@ -10,6 +10,7 @@ namespace Silk.NET.Vulkan; [NativeName("VkDebugReportCallbackEXT")] public readonly unsafe partial struct DebugReportCallbackHandleEXT + : IEquatable { public readonly void* Handle; diff --git a/sources/Vulkan/Vulkan/Handles/DebugUtilsMessengerHandleEXT.gen.cs b/sources/Vulkan/Vulkan/Handles/DebugUtilsMessengerHandleEXT.gen.cs index ba82694671..fb076e55f9 100644 --- a/sources/Vulkan/Vulkan/Handles/DebugUtilsMessengerHandleEXT.gen.cs +++ b/sources/Vulkan/Vulkan/Handles/DebugUtilsMessengerHandleEXT.gen.cs @@ -10,6 +10,7 @@ namespace Silk.NET.Vulkan; [NativeName("VkDebugUtilsMessengerEXT")] public readonly unsafe partial struct DebugUtilsMessengerHandleEXT + : IEquatable { public readonly void* Handle; diff --git a/sources/Vulkan/Vulkan/Handles/DeferredOperationHandleKHR.gen.cs b/sources/Vulkan/Vulkan/Handles/DeferredOperationHandleKHR.gen.cs index f434144c21..0b6acfe23e 100644 --- a/sources/Vulkan/Vulkan/Handles/DeferredOperationHandleKHR.gen.cs +++ b/sources/Vulkan/Vulkan/Handles/DeferredOperationHandleKHR.gen.cs @@ -10,6 +10,7 @@ namespace Silk.NET.Vulkan; [NativeName("VkDeferredOperationKHR")] public readonly unsafe partial struct DeferredOperationHandleKHR + : IEquatable { public readonly void* Handle; diff --git a/sources/Vulkan/Vulkan/Handles/DescriptorPoolHandle.gen.cs b/sources/Vulkan/Vulkan/Handles/DescriptorPoolHandle.gen.cs index e26a03f71d..81aa517e0c 100644 --- a/sources/Vulkan/Vulkan/Handles/DescriptorPoolHandle.gen.cs +++ b/sources/Vulkan/Vulkan/Handles/DescriptorPoolHandle.gen.cs @@ -9,7 +9,7 @@ namespace Silk.NET.Vulkan; [NativeName("VkDescriptorPool")] -public readonly unsafe partial struct DescriptorPoolHandle +public readonly unsafe partial struct DescriptorPoolHandle : IEquatable { public readonly void* Handle; diff --git a/sources/Vulkan/Vulkan/Handles/DescriptorSetHandle.gen.cs b/sources/Vulkan/Vulkan/Handles/DescriptorSetHandle.gen.cs index 6dbf7d78be..874c5c8e29 100644 --- a/sources/Vulkan/Vulkan/Handles/DescriptorSetHandle.gen.cs +++ b/sources/Vulkan/Vulkan/Handles/DescriptorSetHandle.gen.cs @@ -9,7 +9,7 @@ namespace Silk.NET.Vulkan; [NativeName("VkDescriptorSet")] -public readonly unsafe partial struct DescriptorSetHandle +public readonly unsafe partial struct DescriptorSetHandle : IEquatable { public readonly void* Handle; diff --git a/sources/Vulkan/Vulkan/Handles/DescriptorSetLayoutHandle.gen.cs b/sources/Vulkan/Vulkan/Handles/DescriptorSetLayoutHandle.gen.cs index 1da304d04e..d143152f82 100644 --- a/sources/Vulkan/Vulkan/Handles/DescriptorSetLayoutHandle.gen.cs +++ b/sources/Vulkan/Vulkan/Handles/DescriptorSetLayoutHandle.gen.cs @@ -10,6 +10,7 @@ namespace Silk.NET.Vulkan; [NativeName("VkDescriptorSetLayout")] public readonly unsafe partial struct DescriptorSetLayoutHandle + : IEquatable { public readonly void* Handle; diff --git a/sources/Vulkan/Vulkan/Handles/DescriptorUpdateTemplateHandle.gen.cs b/sources/Vulkan/Vulkan/Handles/DescriptorUpdateTemplateHandle.gen.cs index 0406977bf4..363a66fd97 100644 --- a/sources/Vulkan/Vulkan/Handles/DescriptorUpdateTemplateHandle.gen.cs +++ b/sources/Vulkan/Vulkan/Handles/DescriptorUpdateTemplateHandle.gen.cs @@ -10,6 +10,7 @@ namespace Silk.NET.Vulkan; [NativeName("VkDescriptorUpdateTemplate")] public readonly unsafe partial struct DescriptorUpdateTemplateHandle + : IEquatable { public readonly void* Handle; diff --git a/sources/Vulkan/Vulkan/Handles/DeviceHandle.gen.cs b/sources/Vulkan/Vulkan/Handles/DeviceHandle.gen.cs index 9ed0681e99..1152d2fb72 100644 --- a/sources/Vulkan/Vulkan/Handles/DeviceHandle.gen.cs +++ b/sources/Vulkan/Vulkan/Handles/DeviceHandle.gen.cs @@ -9,7 +9,7 @@ namespace Silk.NET.Vulkan; [NativeName("VkDevice")] -public readonly unsafe partial struct DeviceHandle +public readonly unsafe partial struct DeviceHandle : IEquatable { public readonly void* Handle; diff --git a/sources/Vulkan/Vulkan/Handles/DeviceMemoryHandle.gen.cs b/sources/Vulkan/Vulkan/Handles/DeviceMemoryHandle.gen.cs index 7ba8e70aac..4a83741a28 100644 --- a/sources/Vulkan/Vulkan/Handles/DeviceMemoryHandle.gen.cs +++ b/sources/Vulkan/Vulkan/Handles/DeviceMemoryHandle.gen.cs @@ -9,7 +9,7 @@ namespace Silk.NET.Vulkan; [NativeName("VkDeviceMemory")] -public readonly unsafe partial struct DeviceMemoryHandle +public readonly unsafe partial struct DeviceMemoryHandle : IEquatable { public readonly void* Handle; diff --git a/sources/Vulkan/Vulkan/Handles/DisplayHandleKHR.gen.cs b/sources/Vulkan/Vulkan/Handles/DisplayHandleKHR.gen.cs index b24b1bba43..c3059547ff 100644 --- a/sources/Vulkan/Vulkan/Handles/DisplayHandleKHR.gen.cs +++ b/sources/Vulkan/Vulkan/Handles/DisplayHandleKHR.gen.cs @@ -9,7 +9,7 @@ namespace Silk.NET.Vulkan; [NativeName("VkDisplayKHR")] -public readonly unsafe partial struct DisplayHandleKHR +public readonly unsafe partial struct DisplayHandleKHR : IEquatable { public readonly void* Handle; diff --git a/sources/Vulkan/Vulkan/Handles/DisplayModeHandleKHR.gen.cs b/sources/Vulkan/Vulkan/Handles/DisplayModeHandleKHR.gen.cs index 14fda9f162..a799fca91e 100644 --- a/sources/Vulkan/Vulkan/Handles/DisplayModeHandleKHR.gen.cs +++ b/sources/Vulkan/Vulkan/Handles/DisplayModeHandleKHR.gen.cs @@ -9,7 +9,7 @@ namespace Silk.NET.Vulkan; [NativeName("VkDisplayModeKHR")] -public readonly unsafe partial struct DisplayModeHandleKHR +public readonly unsafe partial struct DisplayModeHandleKHR : IEquatable { public readonly void* Handle; diff --git a/sources/Vulkan/Vulkan/Handles/EventHandle.gen.cs b/sources/Vulkan/Vulkan/Handles/EventHandle.gen.cs index f7ffb1943a..bc87c05bab 100644 --- a/sources/Vulkan/Vulkan/Handles/EventHandle.gen.cs +++ b/sources/Vulkan/Vulkan/Handles/EventHandle.gen.cs @@ -9,7 +9,7 @@ namespace Silk.NET.Vulkan; [NativeName("VkEvent")] -public readonly unsafe partial struct EventHandle +public readonly unsafe partial struct EventHandle : IEquatable { public readonly void* Handle; diff --git a/sources/Vulkan/Vulkan/Handles/ExternalComputeQueueHandleNV.gen.cs b/sources/Vulkan/Vulkan/Handles/ExternalComputeQueueHandleNV.gen.cs index e7458c5252..f88983bfdb 100644 --- a/sources/Vulkan/Vulkan/Handles/ExternalComputeQueueHandleNV.gen.cs +++ b/sources/Vulkan/Vulkan/Handles/ExternalComputeQueueHandleNV.gen.cs @@ -10,6 +10,7 @@ namespace Silk.NET.Vulkan; [NativeName("VkExternalComputeQueueNV")] public readonly unsafe partial struct ExternalComputeQueueHandleNV + : IEquatable { public readonly void* Handle; diff --git a/sources/Vulkan/Vulkan/Handles/FenceHandle.gen.cs b/sources/Vulkan/Vulkan/Handles/FenceHandle.gen.cs index 5c58f15b3a..2c70b404d8 100644 --- a/sources/Vulkan/Vulkan/Handles/FenceHandle.gen.cs +++ b/sources/Vulkan/Vulkan/Handles/FenceHandle.gen.cs @@ -9,7 +9,7 @@ namespace Silk.NET.Vulkan; [NativeName("VkFence")] -public readonly unsafe partial struct FenceHandle +public readonly unsafe partial struct FenceHandle : IEquatable { public readonly void* Handle; diff --git a/sources/Vulkan/Vulkan/Handles/FramebufferHandle.gen.cs b/sources/Vulkan/Vulkan/Handles/FramebufferHandle.gen.cs index 7b74cde225..c4cfd8bce5 100644 --- a/sources/Vulkan/Vulkan/Handles/FramebufferHandle.gen.cs +++ b/sources/Vulkan/Vulkan/Handles/FramebufferHandle.gen.cs @@ -9,7 +9,7 @@ namespace Silk.NET.Vulkan; [NativeName("VkFramebuffer")] -public readonly unsafe partial struct FramebufferHandle +public readonly unsafe partial struct FramebufferHandle : IEquatable { public readonly void* Handle; diff --git a/sources/Vulkan/Vulkan/Handles/ImageHandle.gen.cs b/sources/Vulkan/Vulkan/Handles/ImageHandle.gen.cs index 155bd385c9..a94961a9b1 100644 --- a/sources/Vulkan/Vulkan/Handles/ImageHandle.gen.cs +++ b/sources/Vulkan/Vulkan/Handles/ImageHandle.gen.cs @@ -9,7 +9,7 @@ namespace Silk.NET.Vulkan; [NativeName("VkImage")] -public readonly unsafe partial struct ImageHandle +public readonly unsafe partial struct ImageHandle : IEquatable { public readonly void* Handle; diff --git a/sources/Vulkan/Vulkan/Handles/ImageViewHandle.gen.cs b/sources/Vulkan/Vulkan/Handles/ImageViewHandle.gen.cs index 0af6321e26..5e8c796732 100644 --- a/sources/Vulkan/Vulkan/Handles/ImageViewHandle.gen.cs +++ b/sources/Vulkan/Vulkan/Handles/ImageViewHandle.gen.cs @@ -9,7 +9,7 @@ namespace Silk.NET.Vulkan; [NativeName("VkImageView")] -public readonly unsafe partial struct ImageViewHandle +public readonly unsafe partial struct ImageViewHandle : IEquatable { public readonly void* Handle; diff --git a/sources/Vulkan/Vulkan/Handles/IndirectCommandsLayoutHandleEXT.gen.cs b/sources/Vulkan/Vulkan/Handles/IndirectCommandsLayoutHandleEXT.gen.cs index e7bd1380fa..48457ba646 100644 --- a/sources/Vulkan/Vulkan/Handles/IndirectCommandsLayoutHandleEXT.gen.cs +++ b/sources/Vulkan/Vulkan/Handles/IndirectCommandsLayoutHandleEXT.gen.cs @@ -10,6 +10,7 @@ namespace Silk.NET.Vulkan; [NativeName("VkIndirectCommandsLayoutEXT")] public readonly unsafe partial struct IndirectCommandsLayoutHandleEXT + : IEquatable { public readonly void* Handle; diff --git a/sources/Vulkan/Vulkan/Handles/IndirectCommandsLayoutHandleNV.gen.cs b/sources/Vulkan/Vulkan/Handles/IndirectCommandsLayoutHandleNV.gen.cs index 19ac887c9b..dd6c43bd8d 100644 --- a/sources/Vulkan/Vulkan/Handles/IndirectCommandsLayoutHandleNV.gen.cs +++ b/sources/Vulkan/Vulkan/Handles/IndirectCommandsLayoutHandleNV.gen.cs @@ -10,6 +10,7 @@ namespace Silk.NET.Vulkan; [NativeName("VkIndirectCommandsLayoutNV")] public readonly unsafe partial struct IndirectCommandsLayoutHandleNV + : IEquatable { public readonly void* Handle; diff --git a/sources/Vulkan/Vulkan/Handles/IndirectExecutionSetHandleEXT.gen.cs b/sources/Vulkan/Vulkan/Handles/IndirectExecutionSetHandleEXT.gen.cs index e961f08da0..b8fe5999fd 100644 --- a/sources/Vulkan/Vulkan/Handles/IndirectExecutionSetHandleEXT.gen.cs +++ b/sources/Vulkan/Vulkan/Handles/IndirectExecutionSetHandleEXT.gen.cs @@ -10,6 +10,7 @@ namespace Silk.NET.Vulkan; [NativeName("VkIndirectExecutionSetEXT")] public readonly unsafe partial struct IndirectExecutionSetHandleEXT + : IEquatable { public readonly void* Handle; diff --git a/sources/Vulkan/Vulkan/Handles/InstanceHandle.gen.cs b/sources/Vulkan/Vulkan/Handles/InstanceHandle.gen.cs index 0e9b81b12b..54213c139f 100644 --- a/sources/Vulkan/Vulkan/Handles/InstanceHandle.gen.cs +++ b/sources/Vulkan/Vulkan/Handles/InstanceHandle.gen.cs @@ -9,7 +9,7 @@ namespace Silk.NET.Vulkan; [NativeName("VkInstance")] -public readonly unsafe partial struct InstanceHandle +public readonly unsafe partial struct InstanceHandle : IEquatable { public readonly void* Handle; diff --git a/sources/Vulkan/Vulkan/Handles/MicromapHandleEXT.gen.cs b/sources/Vulkan/Vulkan/Handles/MicromapHandleEXT.gen.cs index a97f82b265..ede3548e8b 100644 --- a/sources/Vulkan/Vulkan/Handles/MicromapHandleEXT.gen.cs +++ b/sources/Vulkan/Vulkan/Handles/MicromapHandleEXT.gen.cs @@ -9,7 +9,7 @@ namespace Silk.NET.Vulkan; [NativeName("VkMicromapEXT")] -public readonly unsafe partial struct MicromapHandleEXT +public readonly unsafe partial struct MicromapHandleEXT : IEquatable { public readonly void* Handle; diff --git a/sources/Vulkan/Vulkan/Handles/OpticalFlowSessionHandleNV.gen.cs b/sources/Vulkan/Vulkan/Handles/OpticalFlowSessionHandleNV.gen.cs index bca03c2e28..b4a0db4630 100644 --- a/sources/Vulkan/Vulkan/Handles/OpticalFlowSessionHandleNV.gen.cs +++ b/sources/Vulkan/Vulkan/Handles/OpticalFlowSessionHandleNV.gen.cs @@ -10,6 +10,7 @@ namespace Silk.NET.Vulkan; [NativeName("VkOpticalFlowSessionNV")] public readonly unsafe partial struct OpticalFlowSessionHandleNV + : IEquatable { public readonly void* Handle; diff --git a/sources/Vulkan/Vulkan/Handles/PerformanceConfigurationHandleINTEL.gen.cs b/sources/Vulkan/Vulkan/Handles/PerformanceConfigurationHandleINTEL.gen.cs index 6a9b08f502..152a66483b 100644 --- a/sources/Vulkan/Vulkan/Handles/PerformanceConfigurationHandleINTEL.gen.cs +++ b/sources/Vulkan/Vulkan/Handles/PerformanceConfigurationHandleINTEL.gen.cs @@ -10,6 +10,7 @@ namespace Silk.NET.Vulkan; [NativeName("VkPerformanceConfigurationINTEL")] public readonly unsafe partial struct PerformanceConfigurationHandleINTEL + : IEquatable { public readonly void* Handle; diff --git a/sources/Vulkan/Vulkan/Handles/PhysicalDeviceHandle.gen.cs b/sources/Vulkan/Vulkan/Handles/PhysicalDeviceHandle.gen.cs index c90993f1ad..eca4c5bb5e 100644 --- a/sources/Vulkan/Vulkan/Handles/PhysicalDeviceHandle.gen.cs +++ b/sources/Vulkan/Vulkan/Handles/PhysicalDeviceHandle.gen.cs @@ -9,7 +9,7 @@ namespace Silk.NET.Vulkan; [NativeName("VkPhysicalDevice")] -public readonly unsafe partial struct PhysicalDeviceHandle +public readonly unsafe partial struct PhysicalDeviceHandle : IEquatable { public readonly void* Handle; diff --git a/sources/Vulkan/Vulkan/Handles/PipelineBinaryHandleKHR.gen.cs b/sources/Vulkan/Vulkan/Handles/PipelineBinaryHandleKHR.gen.cs index f265c168b3..9591593f76 100644 --- a/sources/Vulkan/Vulkan/Handles/PipelineBinaryHandleKHR.gen.cs +++ b/sources/Vulkan/Vulkan/Handles/PipelineBinaryHandleKHR.gen.cs @@ -9,7 +9,7 @@ namespace Silk.NET.Vulkan; [NativeName("VkPipelineBinaryKHR")] -public readonly unsafe partial struct PipelineBinaryHandleKHR +public readonly unsafe partial struct PipelineBinaryHandleKHR : IEquatable { public readonly void* Handle; diff --git a/sources/Vulkan/Vulkan/Handles/PipelineCacheHandle.gen.cs b/sources/Vulkan/Vulkan/Handles/PipelineCacheHandle.gen.cs index 4f36c1b0d0..9711010cb4 100644 --- a/sources/Vulkan/Vulkan/Handles/PipelineCacheHandle.gen.cs +++ b/sources/Vulkan/Vulkan/Handles/PipelineCacheHandle.gen.cs @@ -9,7 +9,7 @@ namespace Silk.NET.Vulkan; [NativeName("VkPipelineCache")] -public readonly unsafe partial struct PipelineCacheHandle +public readonly unsafe partial struct PipelineCacheHandle : IEquatable { public readonly void* Handle; diff --git a/sources/Vulkan/Vulkan/Handles/PipelineHandle.gen.cs b/sources/Vulkan/Vulkan/Handles/PipelineHandle.gen.cs index 958f87e6e6..e16111cdb2 100644 --- a/sources/Vulkan/Vulkan/Handles/PipelineHandle.gen.cs +++ b/sources/Vulkan/Vulkan/Handles/PipelineHandle.gen.cs @@ -9,7 +9,7 @@ namespace Silk.NET.Vulkan; [NativeName("VkPipeline")] -public readonly unsafe partial struct PipelineHandle +public readonly unsafe partial struct PipelineHandle : IEquatable { public readonly void* Handle; diff --git a/sources/Vulkan/Vulkan/Handles/PipelineLayoutHandle.gen.cs b/sources/Vulkan/Vulkan/Handles/PipelineLayoutHandle.gen.cs index 318bf3bbf3..cb281c1bfe 100644 --- a/sources/Vulkan/Vulkan/Handles/PipelineLayoutHandle.gen.cs +++ b/sources/Vulkan/Vulkan/Handles/PipelineLayoutHandle.gen.cs @@ -9,7 +9,7 @@ namespace Silk.NET.Vulkan; [NativeName("VkPipelineLayout")] -public readonly unsafe partial struct PipelineLayoutHandle +public readonly unsafe partial struct PipelineLayoutHandle : IEquatable { public readonly void* Handle; diff --git a/sources/Vulkan/Vulkan/Handles/PrivateDataSlotHandle.gen.cs b/sources/Vulkan/Vulkan/Handles/PrivateDataSlotHandle.gen.cs index 05caee5e6f..443605f22b 100644 --- a/sources/Vulkan/Vulkan/Handles/PrivateDataSlotHandle.gen.cs +++ b/sources/Vulkan/Vulkan/Handles/PrivateDataSlotHandle.gen.cs @@ -9,7 +9,7 @@ namespace Silk.NET.Vulkan; [NativeName("VkPrivateDataSlot")] -public readonly unsafe partial struct PrivateDataSlotHandle +public readonly unsafe partial struct PrivateDataSlotHandle : IEquatable { public readonly void* Handle; diff --git a/sources/Vulkan/Vulkan/Handles/QueryPoolHandle.gen.cs b/sources/Vulkan/Vulkan/Handles/QueryPoolHandle.gen.cs index dc596517a8..4dd7c80b63 100644 --- a/sources/Vulkan/Vulkan/Handles/QueryPoolHandle.gen.cs +++ b/sources/Vulkan/Vulkan/Handles/QueryPoolHandle.gen.cs @@ -9,7 +9,7 @@ namespace Silk.NET.Vulkan; [NativeName("VkQueryPool")] -public readonly unsafe partial struct QueryPoolHandle +public readonly unsafe partial struct QueryPoolHandle : IEquatable { public readonly void* Handle; diff --git a/sources/Vulkan/Vulkan/Handles/QueueHandle.gen.cs b/sources/Vulkan/Vulkan/Handles/QueueHandle.gen.cs index e384979c17..6bd5619726 100644 --- a/sources/Vulkan/Vulkan/Handles/QueueHandle.gen.cs +++ b/sources/Vulkan/Vulkan/Handles/QueueHandle.gen.cs @@ -9,7 +9,7 @@ namespace Silk.NET.Vulkan; [NativeName("VkQueue")] -public readonly unsafe partial struct QueueHandle +public readonly unsafe partial struct QueueHandle : IEquatable { public readonly void* Handle; diff --git a/sources/Vulkan/Vulkan/Handles/RenderPassHandle.gen.cs b/sources/Vulkan/Vulkan/Handles/RenderPassHandle.gen.cs index d511ff3ee9..c2de29a3c3 100644 --- a/sources/Vulkan/Vulkan/Handles/RenderPassHandle.gen.cs +++ b/sources/Vulkan/Vulkan/Handles/RenderPassHandle.gen.cs @@ -9,7 +9,7 @@ namespace Silk.NET.Vulkan; [NativeName("VkRenderPass")] -public readonly unsafe partial struct RenderPassHandle +public readonly unsafe partial struct RenderPassHandle : IEquatable { public readonly void* Handle; diff --git a/sources/Vulkan/Vulkan/Handles/SamplerHandle.gen.cs b/sources/Vulkan/Vulkan/Handles/SamplerHandle.gen.cs index 82bc961d77..7ca94c5470 100644 --- a/sources/Vulkan/Vulkan/Handles/SamplerHandle.gen.cs +++ b/sources/Vulkan/Vulkan/Handles/SamplerHandle.gen.cs @@ -9,7 +9,7 @@ namespace Silk.NET.Vulkan; [NativeName("VkSampler")] -public readonly unsafe partial struct SamplerHandle +public readonly unsafe partial struct SamplerHandle : IEquatable { public readonly void* Handle; diff --git a/sources/Vulkan/Vulkan/Handles/SamplerYcbcrConversionHandle.gen.cs b/sources/Vulkan/Vulkan/Handles/SamplerYcbcrConversionHandle.gen.cs index 07fef2a2b0..30dec14482 100644 --- a/sources/Vulkan/Vulkan/Handles/SamplerYcbcrConversionHandle.gen.cs +++ b/sources/Vulkan/Vulkan/Handles/SamplerYcbcrConversionHandle.gen.cs @@ -10,6 +10,7 @@ namespace Silk.NET.Vulkan; [NativeName("VkSamplerYcbcrConversion")] public readonly unsafe partial struct SamplerYcbcrConversionHandle + : IEquatable { public readonly void* Handle; diff --git a/sources/Vulkan/Vulkan/Handles/SemaphoreHandle.gen.cs b/sources/Vulkan/Vulkan/Handles/SemaphoreHandle.gen.cs index 5df2a58297..c12311a17b 100644 --- a/sources/Vulkan/Vulkan/Handles/SemaphoreHandle.gen.cs +++ b/sources/Vulkan/Vulkan/Handles/SemaphoreHandle.gen.cs @@ -9,7 +9,7 @@ namespace Silk.NET.Vulkan; [NativeName("VkSemaphore")] -public readonly unsafe partial struct SemaphoreHandle +public readonly unsafe partial struct SemaphoreHandle : IEquatable { public readonly void* Handle; diff --git a/sources/Vulkan/Vulkan/Handles/ShaderHandleEXT.gen.cs b/sources/Vulkan/Vulkan/Handles/ShaderHandleEXT.gen.cs index 2dcc8dedc6..6fa17c6360 100644 --- a/sources/Vulkan/Vulkan/Handles/ShaderHandleEXT.gen.cs +++ b/sources/Vulkan/Vulkan/Handles/ShaderHandleEXT.gen.cs @@ -9,7 +9,7 @@ namespace Silk.NET.Vulkan; [NativeName("VkShaderEXT")] -public readonly unsafe partial struct ShaderHandleEXT +public readonly unsafe partial struct ShaderHandleEXT : IEquatable { public readonly void* Handle; diff --git a/sources/Vulkan/Vulkan/Handles/ShaderModuleHandle.gen.cs b/sources/Vulkan/Vulkan/Handles/ShaderModuleHandle.gen.cs index 9d26c0f67f..012d3f16cb 100644 --- a/sources/Vulkan/Vulkan/Handles/ShaderModuleHandle.gen.cs +++ b/sources/Vulkan/Vulkan/Handles/ShaderModuleHandle.gen.cs @@ -9,7 +9,7 @@ namespace Silk.NET.Vulkan; [NativeName("VkShaderModule")] -public readonly unsafe partial struct ShaderModuleHandle +public readonly unsafe partial struct ShaderModuleHandle : IEquatable { public readonly void* Handle; diff --git a/sources/Vulkan/Vulkan/Handles/SurfaceHandleKHR.gen.cs b/sources/Vulkan/Vulkan/Handles/SurfaceHandleKHR.gen.cs index dfe65c6351..c9bebce0dd 100644 --- a/sources/Vulkan/Vulkan/Handles/SurfaceHandleKHR.gen.cs +++ b/sources/Vulkan/Vulkan/Handles/SurfaceHandleKHR.gen.cs @@ -9,7 +9,7 @@ namespace Silk.NET.Vulkan; [NativeName("VkSurfaceKHR")] -public readonly unsafe partial struct SurfaceHandleKHR +public readonly unsafe partial struct SurfaceHandleKHR : IEquatable { public readonly void* Handle; diff --git a/sources/Vulkan/Vulkan/Handles/SwapchainHandleKHR.gen.cs b/sources/Vulkan/Vulkan/Handles/SwapchainHandleKHR.gen.cs index 23fbfb7afb..5ad23268fa 100644 --- a/sources/Vulkan/Vulkan/Handles/SwapchainHandleKHR.gen.cs +++ b/sources/Vulkan/Vulkan/Handles/SwapchainHandleKHR.gen.cs @@ -9,7 +9,7 @@ namespace Silk.NET.Vulkan; [NativeName("VkSwapchainKHR")] -public readonly unsafe partial struct SwapchainHandleKHR +public readonly unsafe partial struct SwapchainHandleKHR : IEquatable { public readonly void* Handle; diff --git a/sources/Vulkan/Vulkan/Handles/TensorHandleARM.gen.cs b/sources/Vulkan/Vulkan/Handles/TensorHandleARM.gen.cs index 2ea1b5a006..5bba8062cb 100644 --- a/sources/Vulkan/Vulkan/Handles/TensorHandleARM.gen.cs +++ b/sources/Vulkan/Vulkan/Handles/TensorHandleARM.gen.cs @@ -9,7 +9,7 @@ namespace Silk.NET.Vulkan; [NativeName("VkTensorARM")] -public readonly unsafe partial struct TensorHandleARM +public readonly unsafe partial struct TensorHandleARM : IEquatable { public readonly void* Handle; diff --git a/sources/Vulkan/Vulkan/Handles/TensorViewHandleARM.gen.cs b/sources/Vulkan/Vulkan/Handles/TensorViewHandleARM.gen.cs index 49c93f1b65..893871df46 100644 --- a/sources/Vulkan/Vulkan/Handles/TensorViewHandleARM.gen.cs +++ b/sources/Vulkan/Vulkan/Handles/TensorViewHandleARM.gen.cs @@ -9,7 +9,7 @@ namespace Silk.NET.Vulkan; [NativeName("VkTensorViewARM")] -public readonly unsafe partial struct TensorViewHandleARM +public readonly unsafe partial struct TensorViewHandleARM : IEquatable { public readonly void* Handle; diff --git a/sources/Vulkan/Vulkan/Handles/ValidationCacheHandleEXT.gen.cs b/sources/Vulkan/Vulkan/Handles/ValidationCacheHandleEXT.gen.cs index 3c44743b29..2bcdc08683 100644 --- a/sources/Vulkan/Vulkan/Handles/ValidationCacheHandleEXT.gen.cs +++ b/sources/Vulkan/Vulkan/Handles/ValidationCacheHandleEXT.gen.cs @@ -10,6 +10,7 @@ namespace Silk.NET.Vulkan; [NativeName("VkValidationCacheEXT")] public readonly unsafe partial struct ValidationCacheHandleEXT + : IEquatable { public readonly void* Handle; diff --git a/sources/Vulkan/Vulkan/Handles/VideoSessionHandleKHR.gen.cs b/sources/Vulkan/Vulkan/Handles/VideoSessionHandleKHR.gen.cs index 95217381bd..084dc88d43 100644 --- a/sources/Vulkan/Vulkan/Handles/VideoSessionHandleKHR.gen.cs +++ b/sources/Vulkan/Vulkan/Handles/VideoSessionHandleKHR.gen.cs @@ -9,7 +9,7 @@ namespace Silk.NET.Vulkan; [NativeName("VkVideoSessionKHR")] -public readonly unsafe partial struct VideoSessionHandleKHR +public readonly unsafe partial struct VideoSessionHandleKHR : IEquatable { public readonly void* Handle; diff --git a/sources/Vulkan/Vulkan/Handles/VideoSessionParametersHandleKHR.gen.cs b/sources/Vulkan/Vulkan/Handles/VideoSessionParametersHandleKHR.gen.cs index a1b97f68bf..6b07c0fe85 100644 --- a/sources/Vulkan/Vulkan/Handles/VideoSessionParametersHandleKHR.gen.cs +++ b/sources/Vulkan/Vulkan/Handles/VideoSessionParametersHandleKHR.gen.cs @@ -10,6 +10,7 @@ namespace Silk.NET.Vulkan; [NativeName("VkVideoSessionParametersKHR")] public readonly unsafe partial struct VideoSessionParametersHandleKHR + : IEquatable { public readonly void* Handle; From d70feb5434a8d5fba913defef9ee1db65fe4384f Mon Sep 17 00:00:00 2001 From: William Chen Date: Tue, 9 Jun 2026 06:58:35 -0400 Subject: [PATCH 37/37] Add TransformHandlesTests --- ...lReferencesAreThroughPointers.verified.txt | 9 ++ ...llyTransformsHandleType_NoDsl.verified.txt | 22 +++ ...yTransformsHandleType_WithDsl.verified.txt | 26 ++++ .../SilkTouch/TransformHandlesTests.cs | 127 ++++++++++++++++++ 4 files changed, 184 insertions(+) create mode 100644 tests/SilkTouch/SilkTouch/TransformHandlesTests.DoesNotTransform_WhenNotAllReferencesAreThroughPointers.verified.txt create mode 100644 tests/SilkTouch/SilkTouch/TransformHandlesTests.SuccessfullyTransformsHandleType_NoDsl.verified.txt create mode 100644 tests/SilkTouch/SilkTouch/TransformHandlesTests.SuccessfullyTransformsHandleType_WithDsl.verified.txt create mode 100644 tests/SilkTouch/SilkTouch/TransformHandlesTests.cs diff --git a/tests/SilkTouch/SilkTouch/TransformHandlesTests.DoesNotTransform_WhenNotAllReferencesAreThroughPointers.verified.txt b/tests/SilkTouch/SilkTouch/TransformHandlesTests.DoesNotTransform_WhenNotAllReferencesAreThroughPointers.verified.txt new file mode 100644 index 0000000000..9d3bd3862f --- /dev/null +++ b/tests/SilkTouch/SilkTouch/TransformHandlesTests.DoesNotTransform_WhenNotAllReferencesAreThroughPointers.verified.txt @@ -0,0 +1,9 @@ +// Vk.gen.cs +public struct VkInstance_T +{ +} + +public class Vk +{ + public static extern VkResult vkCreateInstance(VkInstance_T pInstance, VkInstance_T* pInstance, VkInstance_T** pInstance); +} diff --git a/tests/SilkTouch/SilkTouch/TransformHandlesTests.SuccessfullyTransformsHandleType_NoDsl.verified.txt b/tests/SilkTouch/SilkTouch/TransformHandlesTests.SuccessfullyTransformsHandleType_NoDsl.verified.txt new file mode 100644 index 0000000000..d8c9e87daf --- /dev/null +++ b/tests/SilkTouch/SilkTouch/TransformHandlesTests.SuccessfullyTransformsHandleType_NoDsl.verified.txt @@ -0,0 +1,22 @@ +// Vk.gen.cs +[NativeName("VkInstance_T")] +[NameAffix("Suffix", "HandleType", "Handle")] +public readonly unsafe partial struct VkInstance_T : IEquatable +{ + public readonly void* Handle; + public VkInstance_T(void* handle) + { + Handle = handle; + } + + public bool Equals(VkInstance_T other) => Handle == other.Handle; + public override bool Equals(object? obj) => obj is VkInstance_T other && Equals(other); + public override int GetHashCode() => HashCode.Combine((nuint)Handle); + public static bool operator ==(VkInstance_T left, VkInstance_T right) => left.Equals(right); + public static bool operator !=(VkInstance_T left, VkInstance_T right) => !left.Equals(right); +} + +public class Vk +{ + public static extern VkResult vkCreateInstance(VkInstance_T pInstance, VkInstance_T* pInstance); +} diff --git a/tests/SilkTouch/SilkTouch/TransformHandlesTests.SuccessfullyTransformsHandleType_WithDsl.verified.txt b/tests/SilkTouch/SilkTouch/TransformHandlesTests.SuccessfullyTransformsHandleType_WithDsl.verified.txt new file mode 100644 index 0000000000..a9b4efe4ef --- /dev/null +++ b/tests/SilkTouch/SilkTouch/TransformHandlesTests.SuccessfullyTransformsHandleType_WithDsl.verified.txt @@ -0,0 +1,26 @@ +// Vk.gen.cs +[NativeName("VkInstance_T")] +[NameAffix("Suffix", "HandleType", "Handle")] +public readonly unsafe partial struct VkInstance_T : IEquatable +{ + public readonly void* Handle; + public VkInstance_T(void* handle) + { + Handle = handle; + } + + public bool Equals(VkInstance_T other) => Handle == other.Handle; + public override bool Equals(object? obj) => obj is VkInstance_T other && Equals(other); + public override int GetHashCode() => HashCode.Combine((nuint)Handle); + public static bool operator ==(VkInstance_T left, VkInstance_T right) => left.Equals(right); + public static bool operator !=(VkInstance_T left, VkInstance_T right) => !left.Equals(right); + public bool Equals(NullPtr _) => Handle is null; + public static bool operator ==(VkInstance_T left, NullPtr right) => left.Equals(right); + public static bool operator !=(VkInstance_T left, NullPtr right) => !left.Equals(right); + public static implicit operator VkInstance_T(NullPtr _) => default; +} + +public class Vk +{ + public static extern VkResult vkCreateInstance(VkInstance_T pInstance, VkInstance_T* pInstance); +} diff --git a/tests/SilkTouch/SilkTouch/TransformHandlesTests.cs b/tests/SilkTouch/SilkTouch/TransformHandlesTests.cs new file mode 100644 index 0000000000..6e8ffb6154 --- /dev/null +++ b/tests/SilkTouch/SilkTouch/TransformHandlesTests.cs @@ -0,0 +1,127 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.Extensions.Logging.Abstractions; +using Silk.NET.SilkTouch.Mods; + +namespace Silk.NET.SilkTouch.UnitTests; + +public class TransformHandlesTests +{ + static TransformHandlesTests() + { + if (!VerifyDiffPlex.Initialized) + { + VerifyDiffPlex.Initialize(); + } + } + + [Test] + public async Task SuccessfullyTransformsHandleType_NoDsl() + { + var inputDocName = "Vk.gen.cs"; + var project = TestUtils + .CreateTestProject() + .AddDocument( + inputDocName, + """ + public struct VkInstance_T { } + + public class Vk + { + public static extern VkResult vkCreateInstance( + VkInstance_T* pInstance, + VkInstance_T** pInstance + ); + } + """ + ) + .Project; + + var context = new DummyModContext() { SourceProject = project }; + + var transformHandles = new TransformHandles( + new DummyOptions( + new TransformHandles.Config() { UseDsl = false } + ), + NullLogger.Instance + ); + + await transformHandles.ExecuteAsync(context); + + await TestUtils.VerifyDocumentsAsync(context.SourceProject.Documents); + } + + [Test] + public async Task SuccessfullyTransformsHandleType_WithDsl() + { + var inputDocName = "Vk.gen.cs"; + var project = TestUtils + .CreateTestProject() + .AddDocument( + inputDocName, + """ + public struct VkInstance_T { } + + public class Vk + { + public static extern VkResult vkCreateInstance( + VkInstance_T* pInstance, + VkInstance_T** pInstance + ); + } + """ + ) + .Project; + + var context = new DummyModContext() { SourceProject = project }; + + var transformHandles = new TransformHandles( + new DummyOptions( + new TransformHandles.Config() { UseDsl = true } + ), + NullLogger.Instance + ); + + await transformHandles.ExecuteAsync(context); + + await TestUtils.VerifyDocumentsAsync(context.SourceProject.Documents); + } + + [Test] + public async Task DoesNotTransform_WhenNotAllReferencesAreThroughPointers() + { + var inputDocName = "Vk.gen.cs"; + var project = TestUtils + .CreateTestProject() + .AddDocument( + inputDocName, + """ + public struct VkInstance_T { } + + public class Vk + { + public static extern VkResult vkCreateInstance( + VkInstance_T pInstance, + VkInstance_T* pInstance, + VkInstance_T** pInstance + ); + } + """ + ) + .Project; + + var context = new DummyModContext() { SourceProject = project }; + + var transformHandles = new TransformHandles( + new DummyOptions( + new TransformHandles.Config() { UseDsl = true } + ), + NullLogger.Instance + ); + + await transformHandles.ExecuteAsync(context); + + await TestUtils.VerifyDocumentsAsync(context.SourceProject.Documents); + } +}