Skip to content

Commit 8ab8550

Browse files
committed
Rename TransformFlags mod to TransformEnums; Move MaxEnum member removal logic to TransformEnums
1 parent 29f96b3 commit 8ab8550

File tree

64 files changed

+265
-501
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

64 files changed

+265
-501
lines changed

.silktouch/ac001027d53000e0.stout

0 Bytes
Binary file not shown.

generator.json

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@
176176
"TransformHandles",
177177
"TransformFunctions",
178178
"TransformProperties",
179-
"TransformFlags",
179+
"TransformEnums",
180180
"AddVTables"
181181
],
182182
"ClangScraper": {
@@ -203,15 +203,7 @@
203203
},
204204
"PrettifyNames": {
205205
"LongAcronymThreshold": 4,
206-
"GlobalPrefixHint": "vk",
207-
"PrefixOverrides": {
208-
"VkFenceImportFlags": "VK_FENCE_IMPORT",
209-
"VkIndirectStateFlagsNV": "VK_INDIRECT_STATE_FLAG",
210-
"VkSemaphoreImportFlags": "VK_SEMAPHORE_IMPORT",
211-
"VkSemaphoreWaitFlags": "VK_SEMAPHORE_WAIT",
212-
"VkSubmitFlags": "VK_SUBMIT",
213-
"VkSurfaceCounterFlagsEXT": "VK_SURFACE_COUNTER"
214-
}
206+
"GlobalPrefixHint": "vk"
215207
},
216208
"AddVTables": {
217209
"VTables": [
@@ -226,6 +218,19 @@
226218
},
227219
"TransformHandles": {
228220
"UseDSL": true
221+
},
222+
"TransformEnums": {
223+
"AddNoneMemberToFlags": true,
224+
"RemoveMembers": [
225+
{
226+
"MemberName": "MaxEnum",
227+
"MemberValue": "0x7FFFFFFF"
228+
},
229+
{
230+
"MemberName": "Invalid",
231+
"MemberValue": "0x7FFFFFFF"
232+
}
233+
]
229234
}
230235
}
231236
}

sources/SilkTouch/SilkTouch/Mods/Common/ModLoader.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ public class ModLoader
2626
nameof(AddApiProfiles) => typeof(AddApiProfiles),
2727
nameof(MixKhronosData) => typeof(MixKhronosData),
2828
nameof(TransformHandles) => typeof(TransformHandles),
29-
nameof(TransformFlags) => typeof(TransformFlags),
29+
nameof(TransformEnums) => typeof(TransformEnums),
3030
nameof(ExtractNestedTyping) => typeof(ExtractNestedTyping),
3131
nameof(TransformProperties) => typeof(TransformProperties),
3232
nameof(ClangScraper) => typeof(ClangScraper),

sources/SilkTouch/SilkTouch/Mods/MixKhronosData.cs

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1927,7 +1927,6 @@ private class EnumRewriterPhase1(JobData job, ILogger logger) : CSharpSyntaxRewr
19271927
/// Finishes renaming FlagBits enums to Flags.
19281928
/// Marks bitmask enums with the [Flags] attribute.
19291929
/// Replaces uint/ulong with the actual enum type for FlagBits/Flags types.
1930-
/// Removes MaxEnum member from enums.
19311930
/// </summary>
19321931
private class EnumRewriterPhase2(JobData job, EnumRewriterPhase1 phase1) : CSharpSyntaxRewriter(true)
19331932
{
@@ -1947,14 +1946,6 @@ private class EnumRewriterPhase2(JobData job, EnumRewriterPhase1 phase1) : CShar
19471946
node = node.WithAttributeLists(node.AttributeLists.Add(flagsAttribute));
19481947
}
19491948

1950-
if (job.Groups.ContainsKey(identifier))
1951-
{
1952-
// Remove MaxEnum member
1953-
node = node.WithMembers([
1954-
..node.Members.Where(member => !member.Identifier.ToString().Contains("MAX_ENUM"))
1955-
]);
1956-
}
1957-
19581949
return base.VisitEnumDeclaration(node);
19591950
}
19601951

Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
1+
using System.Diagnostics;
2+
using System.Globalization;
3+
using System.Text.RegularExpressions;
4+
using Microsoft.CodeAnalysis;
5+
using Microsoft.CodeAnalysis.CSharp;
6+
using Microsoft.CodeAnalysis.CSharp.Syntax;
7+
using Microsoft.Extensions.Options;
8+
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
9+
10+
namespace Silk.NET.SilkTouch.Mods;
11+
12+
/// <summary>
13+
/// Contains various transformations related to enums.
14+
/// </summary>
15+
[ModConfiguration<Configuration>]
16+
public class TransformEnums(IOptionsSnapshot<TransformEnums.Configuration> cfg) : IMod
17+
{
18+
/// <summary>
19+
/// TransformEnums mod configuration.
20+
/// </summary>
21+
public record Configuration
22+
{
23+
/// <summary>
24+
/// Transforms [Flags] enums to have a "None = 0" member if they do not already have an equivalent.
25+
/// </summary>
26+
public bool AddNoneMemberToFlags { get; init; } = false;
27+
28+
/// <summary>
29+
/// Removes enum members that match the enum member filter.
30+
/// </summary>
31+
/// <remarks>
32+
/// This was originally designed to remove max enum value members.
33+
/// </remarks>
34+
public EnumMemberFilterConfiguration[] RemoveMembers { get; init; } = [];
35+
}
36+
37+
/// <summary>
38+
/// Represents a filter used to match enum members.
39+
/// </summary>
40+
public record EnumMemberFilterConfiguration
41+
{
42+
/// <summary>
43+
/// The enum type name must match this regex.
44+
/// </summary>
45+
public string TypeName { get; init; } = ".*";
46+
47+
/// <summary>
48+
/// The enum member name must match this regex.
49+
/// </summary>
50+
public string MemberName { get; init; } = ".*";
51+
52+
/// <summary>
53+
/// The enum member value must match this in value.
54+
/// This value will be parsed as an integer. Hexadecimal is allowed.
55+
/// Use null to disable this filter.
56+
/// </summary>
57+
public string? MemberValue { get; init; }
58+
}
59+
60+
private class EnumMemberFilter
61+
{
62+
public Regex TypeName { get; }
63+
public Regex MemberName { get; }
64+
public long? MemberValue { get; }
65+
66+
public EnumMemberFilter(EnumMemberFilterConfiguration configuration)
67+
{
68+
TypeName = new Regex(configuration.TypeName);
69+
MemberName = new Regex(configuration.MemberName);
70+
71+
if (configuration.MemberValue != null)
72+
{
73+
if (configuration.MemberValue.StartsWith("0x"))
74+
{
75+
MemberValue = long.Parse(configuration.MemberValue["0x".Length..], NumberStyles.AllowHexSpecifier);
76+
}
77+
else
78+
{
79+
MemberValue = long.Parse(configuration.MemberValue);
80+
}
81+
}
82+
}
83+
84+
public bool IsTypeMatch(ITypeSymbol? enumType)
85+
{
86+
return enumType != null && TypeName.IsMatch(enumType.Name);
87+
}
88+
89+
public bool IsMemberMatch(ISymbol? enumMember)
90+
{
91+
if (enumMember is not IFieldSymbol fieldSymbol)
92+
{
93+
return false;
94+
}
95+
96+
if (!MemberName.IsMatch(enumMember.Name))
97+
{
98+
return false;
99+
}
100+
101+
if (MemberValue == null)
102+
{
103+
// Filter is disabled
104+
return true;
105+
}
106+
107+
if (fieldSymbol.ConstantValue == null)
108+
{
109+
// We don't know the constant value for sure
110+
// Return false as a default
111+
return false;
112+
}
113+
114+
return Convert.ToInt64(fieldSymbol.ConstantValue) == MemberValue;
115+
}
116+
}
117+
118+
/// <inheritdoc />
119+
public async Task ExecuteAsync(IModContext ctx, CancellationToken ct = default)
120+
{
121+
var config = cfg.Get(ctx.JobKey);
122+
var removeMemberFilters = config.RemoveMembers.Select(c => new EnumMemberFilter(c)).ToList();
123+
124+
var proj = ctx.SourceProject;
125+
if (proj == null)
126+
{
127+
return;
128+
}
129+
130+
var compilation = await proj.GetCompilationAsync(ct);
131+
if (compilation == null)
132+
{
133+
return;
134+
}
135+
136+
var rewriter = new Rewriter(config, removeMemberFilters, compilation);
137+
foreach (var docId in proj?.DocumentIds ?? [])
138+
{
139+
var doc = proj!.GetDocument(docId) ?? throw new InvalidOperationException("Document missing");
140+
proj = doc.WithSyntaxRoot(
141+
rewriter.Visit(await doc.GetSyntaxRootAsync(ct))?.NormalizeWhitespace()
142+
?? throw new InvalidOperationException("Visit returned null.")
143+
).Project;
144+
}
145+
146+
ctx.SourceProject = proj;
147+
}
148+
149+
private class Rewriter(Configuration config, List<EnumMemberFilter> removeMemberFilters, Compilation compilation) : CSharpSyntaxRewriter
150+
{
151+
public override SyntaxNode? VisitEnumDeclaration(EnumDeclarationSyntax node)
152+
{
153+
var semanticModel = compilation.GetSemanticModel(node.SyntaxTree);
154+
var symbol = semanticModel.GetDeclaredSymbol(node);
155+
if (symbol == null)
156+
{
157+
return base.VisitEnumDeclaration(node);
158+
}
159+
160+
// This list is used to defer the modification of the enum declaration syntax node
161+
// This is important because modifying the node will detach it from the semantic model
162+
var members = node.Members.ToList();
163+
164+
foreach (var filter in removeMemberFilters)
165+
{
166+
if (!filter.IsTypeMatch(symbol))
167+
{
168+
continue;
169+
}
170+
171+
members = members
172+
.Where(member => !filter.IsMemberMatch(semanticModel.GetDeclaredSymbol(member)))
173+
.ToList();
174+
}
175+
176+
var isFlagsEnum = node.AttributeLists.SelectMany(list => list.Attributes)
177+
.Any(attribute => attribute.IsAttribute("System.Flags"));
178+
179+
if (node.Identifier.ToString() == "ClusterAccelerationStructureAddressResolutionFlagsNV")
180+
{
181+
Debugger.Break();
182+
}
183+
184+
if (isFlagsEnum && config.AddNoneMemberToFlags)
185+
{
186+
// Add None member if it doesn't exist yet
187+
var hasNoneMember = symbol.Members().Any(member =>
188+
{
189+
if (member is not IFieldSymbol fieldSymbol)
190+
{
191+
return false;
192+
}
193+
194+
if (member.Name == "None")
195+
{
196+
return true;
197+
}
198+
199+
if (fieldSymbol.ConstantValue == null)
200+
{
201+
// We don't know the constant value for sure
202+
// Return false as a default
203+
return false;
204+
}
205+
206+
return Convert.ToInt64(fieldSymbol.ConstantValue) == 0;
207+
});
208+
209+
if (!hasNoneMember)
210+
{
211+
var noneMember = EnumMemberDeclaration("None")
212+
.WithEqualsValue(
213+
EqualsValueClause(
214+
LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(0))
215+
)
216+
);
217+
218+
members.Insert(0, noneMember);
219+
}
220+
}
221+
222+
node = node.WithMembers([..members]);
223+
224+
return base.VisitEnumDeclaration(node);
225+
}
226+
}
227+
}

0 commit comments

Comments
 (0)