forked from aers/FFXIVClientStructs
-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathCStrOverloadsGenerator.cs
166 lines (136 loc) · 6.94 KB
/
CStrOverloadsGenerator.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
using FFXIVClientStructs.InteropGenerator;
using FFXIVClientStructs.InteropSourceGenerators.Extensions;
using FFXIVClientStructs.InteropSourceGenerators.Models;
using LanguageExt;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
namespace FFXIVClientStructs.InteropSourceGenerators;
[Generator]
internal sealed class CStrOverloadsGenerator : IIncrementalGenerator
{
private const string AttributeName = "FFXIVClientStructs.Interop.Attributes.GenerateCStrOverloadsAttribute";
public void Initialize(IncrementalGeneratorInitializationContext context)
{
IncrementalValuesProvider<(Validation<DiagnosticInfo, StructInfo> StructInfo,
Validation<DiagnosticInfo, CStrOverloadInfo> CStrOverloadInfos)> structAndMethodInfos =
context.SyntaxProvider
.ForAttributeWithMetadataName(
AttributeName,
static (node, _) => node is MethodDeclarationSyntax
{
Parent : StructDeclarationSyntax, AttributeLists.Count: > 0
},
static (context, _) =>
{
StructDeclarationSyntax structSyntax = (StructDeclarationSyntax)context.TargetNode.Parent!;
MethodDeclarationSyntax methodSyntax = (MethodDeclarationSyntax)context.TargetNode;
IMethodSymbol methodSymbol = (IMethodSymbol)context.TargetSymbol;
return (Struct: StructInfo.GetFromSyntax(structSyntax),
Info: CStrOverloadInfo.GetFromRoslyn(methodSyntax, methodSymbol));
});
// group by struct
IncrementalValuesProvider<(Validation<DiagnosticInfo, StructInfo> StructInfo,
Validation<DiagnosticInfo, Seq<CStrOverloadInfo>> CStrOverloadInfos)> groupedStructInfoWithMethodInfos =
structAndMethodInfos.TupleGroupByValidation();
// make sure caching is working
IncrementalValuesProvider<Validation<DiagnosticInfo, StructWithCStrOverloadInfos>> structWithMethodInfos =
groupedStructInfoWithMethodInfos.Select(static (item, _) =>
(item.StructInfo, item.CStrOverloadInfos).Apply(static (si, csoi) =>
new StructWithCStrOverloadInfos(si, csoi))
);
context.RegisterSourceOutput(structWithMethodInfos, (sourceContext, item) =>
{
item.Match(
Fail: diagnosticInfos =>
{
diagnosticInfos.Iter(dInfo => sourceContext.ReportDiagnostic(dInfo.ToDiagnostic()));
},
Succ: structWithMethodInfo =>
{
sourceContext.AddSource(structWithMethodInfo.GetFileName(), structWithMethodInfo.RenderSource());
});
});
}
internal sealed record CStrOverloadInfo(MethodInfo MethodInfo, Option<string> IgnoreArgument)
{
public static Validation<DiagnosticInfo, CStrOverloadInfo> GetFromRoslyn(
MethodDeclarationSyntax methodSyntax, IMethodSymbol methodSymbol)
{
Validation<DiagnosticInfo, MethodInfo> validMethodInfo =
MethodInfo.GetFromRoslyn(methodSyntax, methodSymbol);
Option<string> optionIgnoreArgument =
methodSymbol.GetFirstAttributeDataByTypeName(AttributeName)
.GetValidAttributeArgument<string>("IgnoreArgument", 0, AttributeName, methodSymbol)
.ToOption();
return validMethodInfo.Bind<CStrOverloadInfo>(methodInfo =>
new CStrOverloadInfo(methodInfo, optionIgnoreArgument));
}
public void RenderOverloadMethods(IndentedStringBuilder builder)
{
Seq<string> overloadParamNames =
MethodInfo.Parameters.Where(param => param.Type == "byte*" && param.Name != IgnoreArgument)
.Map(param => param.Name).ToSeq();
string paramNames = MethodInfo.GetParameterNamesString();
foreach (string overloadParamName in overloadParamNames)
paramNames = paramNames.Replace(overloadParamName, $"{overloadParamName}Ptr");
string returnString = MethodInfo.ReturnType == "void" ? string.Empty : "return ";
builder.AppendLine();
MethodInfo.RenderStartOverload(builder, "byte*", "string", IgnoreArgument);
foreach (string overloadParamName in overloadParamNames)
{
var valName = $"utf8StringLength{overloadParamName}";
builder.AppendLine(
$"int {valName} = global::System.Text.Encoding.UTF8.GetByteCount({overloadParamName});");
builder.AppendLine(
$"Span<byte> {overloadParamName}Bytes = {valName} <= 512 ? stackalloc byte[{valName} + 1] : new byte[{valName} + 1];");
builder.AppendLine(
$"global::System.Text.Encoding.UTF8.GetBytes({overloadParamName}, {overloadParamName}Bytes);");
builder.AppendLine($"{overloadParamName}Bytes[{valName}] = 0;");
builder.AppendLine();
}
foreach (string overloadParamName in overloadParamNames)
{
builder.AppendLine($"fixed (byte* {overloadParamName}Ptr = {overloadParamName}Bytes)");
builder.AppendLine("{");
builder.Indent();
}
builder.AppendLine($"{returnString}{MethodInfo.Name}({paramNames});");
foreach (string _ in overloadParamNames)
{
builder.DecrementIndent();
builder.AppendLine("}");
}
MethodInfo.RenderEnd(builder);
builder.AppendLine();
MethodInfo.RenderStartOverload(builder, "byte*", "ReadOnlySpan<byte>", IgnoreArgument);
foreach (string overloadParamName in overloadParamNames)
{
builder.AppendLine($"fixed (byte* {overloadParamName}Ptr = {overloadParamName})");
builder.AppendLine("{");
builder.Indent();
}
builder.AppendLine($"{returnString}{MethodInfo.Name}({paramNames});");
foreach (string _ in overloadParamNames)
{
builder.DecrementIndent();
builder.AppendLine("}");
}
MethodInfo.RenderEnd(builder);
}
}
private sealed record StructWithCStrOverloadInfos(StructInfo StructInfo, Seq<CStrOverloadInfo> CStrOverloadInfos)
{
public string RenderSource()
{
IndentedStringBuilder builder = new();
StructInfo.RenderStart(builder);
CStrOverloadInfos.Iter(csoi => csoi.RenderOverloadMethods(builder));
StructInfo.RenderEnd(builder);
return builder.ToString();
}
public string GetFileName()
{
return $"{StructInfo.Namespace}.{StructInfo.Name}.CStrOverloads.g.cs";
}
}
}