Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/aspnet/MessagePack-CSharp.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'src/MessagePack.GeneratorCore/CodeGenerator.cs')
-rw-r--r--src/MessagePack.GeneratorCore/CodeGenerator.cs317
1 files changed, 193 insertions, 124 deletions
diff --git a/src/MessagePack.GeneratorCore/CodeGenerator.cs b/src/MessagePack.GeneratorCore/CodeGenerator.cs
index f4dd3d35..1f48973e 100644
--- a/src/MessagePack.GeneratorCore/CodeGenerator.cs
+++ b/src/MessagePack.GeneratorCore/CodeGenerator.cs
@@ -2,6 +2,7 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
+using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
@@ -16,15 +17,15 @@ namespace MessagePackCompiler
{
public class CodeGenerator
{
+ private static readonly HashSet<char> InvalidFileCharSet = new(Path.GetInvalidFileNameChars());
+
private static readonly Encoding NoBomUtf8 = new UTF8Encoding(false);
- private Action<string> logger;
- private CancellationToken cancellationToken;
+ private readonly Action<string> logger;
public CodeGenerator(Action<string> logger, CancellationToken cancellationToken)
{
this.logger = logger;
- this.cancellationToken = cancellationToken;
}
/// <summary>
@@ -42,21 +43,21 @@ namespace MessagePackCompiler
Compilation compilation,
string output,
string resolverName,
- string @namespace,
+ string? @namespace,
bool useMapMode,
- string multipleIfDirectiveOutputSymbols,
- string[] externalIgnoreTypeNames)
+ string? multipleIfDirectiveOutputSymbols,
+ string[]? externalIgnoreTypeNames)
{
var namespaceDot = string.IsNullOrWhiteSpace(@namespace) ? string.Empty : @namespace + ".";
var multipleOutputSymbols = multipleIfDirectiveOutputSymbols?.Split(',') ?? Array.Empty<string>();
var sw = Stopwatch.StartNew();
- foreach (var multioutSymbol in multipleOutputSymbols.Length == 0 ? new[] { string.Empty } : multipleOutputSymbols)
+ foreach (var multiOutputSymbol in multipleOutputSymbols.Length == 0 ? new[] { string.Empty } : multipleOutputSymbols)
{
logger("Project Compilation Start:" + compilation.AssemblyName);
- var collector = new TypeCollector(compilation, true, useMapMode, externalIgnoreTypeNames, x => Console.WriteLine(x));
+ var collector = new TypeCollector(compilation, true, useMapMode, externalIgnoreTypeNames, Console.WriteLine);
logger("Project Compilation Complete:" + sw.Elapsed.ToString());
@@ -73,152 +74,220 @@ namespace MessagePackCompiler
if (Path.GetExtension(output) == ".cs")
{
// SingleFile Output
- var objectFormatterTemplates = objectInfo
- .GroupBy(x => (x.Namespace, x.IsStringKey))
- .Select(x =>
- {
- var (nameSpace, isStringKey) = x.Key;
- var objectSerializationInfos = x.ToArray();
- var template = isStringKey ? new StringKeyFormatterTemplate() : (IFormatterTemplate)new FormatterTemplate();
+ var fullGeneratedProgramText = GenerateSingleFileSync(resolverName, namespaceDot, objectInfo, enumInfo, unionInfo, genericInfo);
+ if (multiOutputSymbol == string.Empty)
+ {
+ await OutputAsync(output, fullGeneratedProgramText);
+ }
+ else
+ {
+ var fname = Path.GetFileNameWithoutExtension(output) + "." + MultiSymbolToSafeFilePath(multiOutputSymbol) + ".cs";
+ var text = $"#if {multiOutputSymbol}" + Environment.NewLine + fullGeneratedProgramText + Environment.NewLine + "#endif";
+ await OutputAsync(Path.Combine(Path.GetDirectoryName(output) ?? string.Empty, fname), text);
+ }
+ }
+ else
+ {
+ // Multiple File output
+ await GenerateMultipleFileAsync(output, resolverName, objectInfo, enumInfo, unionInfo, namespaceDot, multiOutputSymbol, genericInfo);
+ }
- template.Namespace = namespaceDot + "Formatters" + (nameSpace is null ? string.Empty : "." + nameSpace);
- template.ObjectSerializationInfos = objectSerializationInfos;
+ if (objectInfo.Length == 0 && enumInfo.Length == 0 && genericInfo.Length == 0 && unionInfo.Length == 0)
+ {
+ logger("Generated result is empty, unexpected result?");
+ }
+ }
- return template;
- })
- .ToArray();
+ logger("Output Generation Complete:" + sw.Elapsed.ToString());
+ }
- var enumFormatterTemplates = enumInfo
- .GroupBy(x => x.Namespace)
- .Select(x => new EnumTemplate()
- {
- Namespace = namespaceDot + "Formatters" + ((x.Key == null) ? string.Empty : "." + x.Key),
- EnumSerializationInfos = x.ToArray(),
- })
- .ToArray();
-
- var unionFormatterTemplates = unionInfo
- .GroupBy(x => x.Namespace)
- .Select(x => new UnionTemplate()
- {
- Namespace = namespaceDot + "Formatters" + ((x.Key == null) ? string.Empty : "." + x.Key),
- UnionSerializationInfos = x.ToArray(),
- })
- .ToArray();
+ /// <summary>
+ /// Generates the specialized resolver and formatters for the types that require serialization in a given compilation.
+ /// </summary>
+ /// <param name="resolverName">The resolver name.</param>
+ /// <param name="namespaceDot">The namespace for the generated type to be created in.</param>
+ /// <param name="objectInfo">The ObjectSerializationInfo array which TypeCollector.Collect returns.</param>
+ /// <param name="enumInfo">The EnumSerializationInfo array which TypeCollector.Collect returns.</param>
+ /// <param name="unionInfo">The UnionSerializationInfo array which TypeCollector.Collect returns.</param>
+ /// <param name="genericInfo">The GenericSerializationInfo array which TypeCollector.Collect returns.</param>
+ public static string GenerateSingleFileSync(string resolverName, string namespaceDot, ObjectSerializationInfo[] objectInfo, EnumSerializationInfo[] enumInfo, UnionSerializationInfo[] unionInfo, GenericSerializationInfo[] genericInfo)
+ {
+ var objectFormatterTemplates = objectInfo
+ .GroupBy(x => (x.Namespace, x.IsStringKey))
+ .Select(x =>
+ {
+ var (nameSpace, isStringKey) = x.Key;
+ var objectSerializationInfos = x.ToArray();
+ var ns = namespaceDot + "Formatters" + (nameSpace is null ? string.Empty : "." + nameSpace);
+ var template = isStringKey ? new StringKeyFormatterTemplate(ns, objectSerializationInfos) : (IFormatterTemplate)new FormatterTemplate(ns, objectSerializationInfos);
+ return template;
+ })
+ .ToArray();
+
+ string GetNamespace<T>(IGrouping<string?, T> x)
+ {
+ if (x.Key == null)
+ {
+ return namespaceDot + "Formatters";
+ }
- var resolverTemplate = new ResolverTemplate()
- {
- Namespace = namespaceDot + "Resolvers",
- FormatterNamespace = namespaceDot + "Formatters",
- ResolverName = resolverName,
- RegisterInfos = genericInfo.Where(x => !x.IsOpenGenericType).Cast<IResolverRegisterInfo>().Concat(enumInfo).Concat(unionInfo).Concat(objectInfo.Where(x => !x.IsOpenGenericType)).ToArray(),
- };
-
- var sb = new StringBuilder();
- sb.AppendLine(resolverTemplate.TransformText());
- sb.AppendLine();
- foreach (var item in enumFormatterTemplates)
- {
- var text = item.TransformText();
- sb.AppendLine(text);
- }
+ return namespaceDot + "Formatters." + x.Key;
+ }
- sb.AppendLine();
- foreach (var item in unionFormatterTemplates)
- {
- var text = item.TransformText();
- sb.AppendLine(text);
- }
+ var enumFormatterTemplates = enumInfo
+ .GroupBy(x => x.Namespace)
+ .Select(x => new EnumTemplate(GetNamespace(x), x.ToArray()))
+ .ToArray();
- sb.AppendLine();
- foreach (var item in objectFormatterTemplates)
- {
- var text = item.TransformText();
- sb.AppendLine(text);
- }
+ var unionFormatterTemplates = unionInfo
+ .GroupBy(x => x.Namespace)
+ .Select(x => new UnionTemplate(GetNamespace(x), x.ToArray()))
+ .ToArray();
- if (multioutSymbol == string.Empty)
- {
- await OutputAsync(output, sb.ToString(), cancellationToken);
- }
- else
+ var resolverTemplate = new ResolverTemplate(namespaceDot + "Resolvers", namespaceDot + "Formatters", resolverName, genericInfo.Where(x => !x.IsOpenGenericType).Cast<IResolverRegisterInfo>().Concat(enumInfo).Concat(unionInfo).Concat(objectInfo.Where(x => !x.IsOpenGenericType)).ToArray());
+
+ var sb = new StringBuilder();
+ sb.AppendLine(resolverTemplate.TransformText());
+ sb.AppendLine();
+ foreach (var item in enumFormatterTemplates)
+ {
+ var text = item.TransformText();
+ sb.AppendLine(text);
+ }
+
+ sb.AppendLine();
+ foreach (var item in unionFormatterTemplates)
+ {
+ var text = item.TransformText();
+ sb.AppendLine(text);
+ }
+
+ sb.AppendLine();
+ foreach (var item in objectFormatterTemplates)
+ {
+ var text = item.TransformText();
+ sb.AppendLine(text);
+ }
+
+ return sb.ToString();
+ }
+
+ private Task GenerateMultipleFileAsync(string output, string resolverName, ObjectSerializationInfo[] objectInfo, EnumSerializationInfo[] enumInfo, UnionSerializationInfo[] unionInfo, string namespaceDot, string multioutSymbol, GenericSerializationInfo[] genericInfo)
+ {
+ string GetNamespace(INamespaceInfo x)
+ {
+ if (x.Namespace == null)
+ {
+ return namespaceDot + "Formatters";
+ }
+
+ return namespaceDot + "Formatters." + x.Namespace;
+ }
+
+ var waitingTasks = new Task[objectInfo.Length + enumInfo.Length + unionInfo.Length + 1];
+ var waitingIndex = 0;
+ foreach (var x in objectInfo)
+ {
+ var ns = namespaceDot + "Formatters" + (x.Namespace is null ? string.Empty : "." + x.Namespace);
+ var template = x.IsStringKey ? new StringKeyFormatterTemplate(ns, new[] { x }) : (IFormatterTemplate)new FormatterTemplate(ns, new[] { x });
+ var text = template.TransformText();
+ waitingTasks[waitingIndex++] = OutputToDirAsync(output, template.Namespace, x.Name + "Formatter", multioutSymbol, text);
+ }
+
+ foreach (var x in enumInfo)
+ {
+ var template = new EnumTemplate(GetNamespace(x), new[] { x });
+ var text = template.TransformText();
+ waitingTasks[waitingIndex++] = OutputToDirAsync(output, template.Namespace, x.Name + "Formatter", multioutSymbol, text);
+ }
+
+ foreach (var x in unionInfo)
+ {
+ var template = new UnionTemplate(GetNamespace(x), new[] { x });
+ var text = template.TransformText();
+ waitingTasks[waitingIndex++] = OutputToDirAsync(output, template.Namespace, x.Name + "Formatter", multioutSymbol, text);
+ }
+
+ var resolverTemplate = new ResolverTemplate(namespaceDot + "Resolvers", namespaceDot + "Formatters", resolverName, genericInfo.Where(x => !x.IsOpenGenericType).Cast<IResolverRegisterInfo>().Concat(enumInfo).Concat(unionInfo).Concat(objectInfo.Where(x => !x.IsOpenGenericType)).ToArray());
+ waitingTasks[waitingIndex] = OutputToDirAsync(output, resolverTemplate.Namespace, resolverTemplate.ResolverName, multioutSymbol, resolverTemplate.TransformText());
+ return Task.WhenAll(waitingTasks);
+ }
+
+ private Task OutputToDirAsync(string dir, string ns, string name, string multipleOutSymbol, string text)
+ {
+ var builder = new StringBuilder();
+ void AppendDir(string dir)
+ {
+ if (dir.Length != 0)
+ {
+ builder.Append(dir);
+ if (dir[dir.Length - 1] != Path.DirectorySeparatorChar && dir[dir.Length - 1] != Path.AltDirectorySeparatorChar)
{
- var fname = Path.GetFileNameWithoutExtension(output) + "." + MultiSymbolToSafeFilePath(multioutSymbol) + ".cs";
- var text = $"#if {multioutSymbol}" + Environment.NewLine + sb.ToString() + Environment.NewLine + "#endif";
- await OutputAsync(Path.Combine(Path.GetDirectoryName(output), fname), text, cancellationToken);
+ builder.Append(Path.DirectorySeparatorChar);
}
}
+ }
+
+ void AppendChar(char c)
+ {
+ if (c == '.' || InvalidFileCharSet.Contains(c))
+ {
+ builder.Append('_');
+ }
else
{
- // Multiple File output
- foreach (var x in objectInfo)
- {
- var template = x.IsStringKey ? new StringKeyFormatterTemplate() : (IFormatterTemplate)new FormatterTemplate();
- template.Namespace = namespaceDot + "Formatters" + (x.Namespace is null ? string.Empty : "." + x.Namespace);
- template.ObjectSerializationInfos = new[] { x };
-
- var text = template.TransformText();
- await OutputToDirAsync(output, template.Namespace, x.Name + "Formatter", multioutSymbol, text, cancellationToken);
- }
+ builder.Append(c);
+ }
+ }
- foreach (var x in enumInfo)
+ void Append(string text)
+ {
+ var span = text.AsSpan();
+ while (!span.IsEmpty)
+ {
+ var index = span.IndexOf("global::".AsSpan());
+ if (index == -1)
{
- var template = new EnumTemplate()
+ foreach (var c in span)
{
- Namespace = namespaceDot + "Formatters" + ((x.Namespace == null) ? string.Empty : "." + x.Namespace),
- EnumSerializationInfos = new[] { x },
- };
+ AppendChar(c);
+ }
- var text = template.TransformText();
- await OutputToDirAsync(output, template.Namespace, x.Name + "Formatter", multioutSymbol, text, cancellationToken);
+ break;
}
- foreach (var x in unionInfo)
+ if (index == 0)
{
- var template = new UnionTemplate()
- {
- Namespace = namespaceDot + "Formatters" + ((x.Namespace == null) ? string.Empty : "." + x.Namespace),
- UnionSerializationInfos = new[] { x },
- };
-
- var text = template.TransformText();
- await OutputToDirAsync(output, template.Namespace, x.Name + "Formatter", multioutSymbol, text, cancellationToken);
+ span = span.Slice("global::".Length);
+ continue;
}
- var resolverTemplate = new ResolverTemplate()
+ foreach (var c in span.Slice(0, index))
{
- Namespace = namespaceDot + "Resolvers",
- FormatterNamespace = namespaceDot + "Formatters",
- ResolverName = resolverName,
- RegisterInfos = genericInfo.Where(x => !x.IsOpenGenericType).Cast<IResolverRegisterInfo>().Concat(enumInfo).Concat(unionInfo).Concat(objectInfo.Where(x => !x.IsOpenGenericType)).ToArray(),
- };
-
- await OutputToDirAsync(output, resolverTemplate.Namespace, resolverTemplate.ResolverName, multioutSymbol, resolverTemplate.TransformText(), cancellationToken);
- }
+ AppendChar(c);
+ }
- if (objectInfo.Length == 0 && enumInfo.Length == 0 && genericInfo.Length == 0 && unionInfo.Length == 0)
- {
- logger("Generated result is empty, unexpected result?");
+ span = span.Slice(index + "global::".Length);
}
}
- logger("Output Generation Complete:" + sw.Elapsed.ToString());
- }
+ AppendDir(dir);
- private Task OutputToDirAsync(string dir, string ns, string name, string multipleOutSymbol, string text, CancellationToken cancellationToken)
- {
- if (multipleOutSymbol == string.Empty)
- {
- return OutputAsync(Path.Combine(dir, $"{ns}_{name}".Replace(".", "_").Replace("global::", string.Empty) + ".cs"), text, cancellationToken);
- }
- else
+ if (!string.IsNullOrWhiteSpace(multipleOutSymbol))
{
text = $"#if {multipleOutSymbol}" + Environment.NewLine + text + Environment.NewLine + "#endif";
- return OutputAsync(Path.Combine(dir, MultiSymbolToSafeFilePath(multipleOutSymbol), $"{ns}_{name}".Replace(".", "_").Replace("global::", string.Empty) + ".cs"), text, cancellationToken);
+ AppendDir(MultiSymbolToSafeFilePath(multipleOutSymbol));
}
+
+ Append(ns);
+ builder.Append('_');
+ Append(name);
+ builder.Append(".cs");
+
+ return OutputAsync(builder.ToString(), text);
}
- private Task OutputAsync(string path, string text, CancellationToken cancellationToken)
+ private Task OutputAsync(string path, string text)
{
path = path.Replace("global::", string.Empty);
@@ -226,12 +295,12 @@ namespace MessagePackCompiler
logger(prefix + path);
var fi = new FileInfo(path);
- if (!fi.Directory.Exists)
+ if (fi.Directory != null && !fi.Directory.Exists)
{
fi.Directory.Create();
}
- System.IO.File.WriteAllText(path, NormalizeNewLines(text), NoBomUtf8);
+ File.WriteAllText(path, NormalizeNewLines(text), NoBomUtf8);
return Task.CompletedTask;
}