From 0ce3c92c357d4524531d78b1b98a23cab5088ba9 Mon Sep 17 00:00:00 2001 From: pCYSl5EDgo <31692496+pCYSl5EDgo@users.noreply.github.com> Date: Sun, 18 Oct 2020 13:45:35 +0900 Subject: [PATCH] Prepare for C# Source Generator (#1082) --- .../CodeGenerator.cs | 241 ++++++++++-------- 1 file changed, 132 insertions(+), 109 deletions(-) diff --git a/src/MessagePack.GeneratorCore/CodeGenerator.cs b/src/MessagePack.GeneratorCore/CodeGenerator.cs index f4dd3d35c..a48528a04 100644 --- a/src/MessagePack.GeneratorCore/CodeGenerator.cs +++ b/src/MessagePack.GeneratorCore/CodeGenerator.cs @@ -73,127 +73,22 @@ public async Task GenerateFileAsync( 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(); - - template.Namespace = namespaceDot + "Formatters" + (nameSpace is null ? string.Empty : "." + nameSpace); - template.ObjectSerializationInfos = objectSerializationInfos; - - return template; - }) - .ToArray(); - - 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(); - - var resolverTemplate = new ResolverTemplate() - { - Namespace = namespaceDot + "Resolvers", - FormatterNamespace = namespaceDot + "Formatters", - ResolverName = resolverName, - RegisterInfos = genericInfo.Where(x => !x.IsOpenGenericType).Cast().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); - } - + var fullGeneratedProgramText = GenerateSingleFileSync(resolverName, namespaceDot, objectInfo, enumInfo, unionInfo, genericInfo); if (multioutSymbol == string.Empty) { - await OutputAsync(output, sb.ToString(), cancellationToken); + await OutputAsync(output, fullGeneratedProgramText, cancellationToken); } else { var fname = Path.GetFileNameWithoutExtension(output) + "." + MultiSymbolToSafeFilePath(multioutSymbol) + ".cs"; - var text = $"#if {multioutSymbol}" + Environment.NewLine + sb.ToString() + Environment.NewLine + "#endif"; + var text = $"#if {multioutSymbol}" + Environment.NewLine + fullGeneratedProgramText + Environment.NewLine + "#endif"; await OutputAsync(Path.Combine(Path.GetDirectoryName(output), fname), text, cancellationToken); } } 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); - } - - foreach (var x in enumInfo) - { - var template = new EnumTemplate() - { - Namespace = namespaceDot + "Formatters" + ((x.Namespace == null) ? string.Empty : "." + x.Namespace), - EnumSerializationInfos = new[] { x }, - }; - - var text = template.TransformText(); - await OutputToDirAsync(output, template.Namespace, x.Name + "Formatter", multioutSymbol, text, cancellationToken); - } - - foreach (var x in unionInfo) - { - 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); - } - - var resolverTemplate = new ResolverTemplate() - { - Namespace = namespaceDot + "Resolvers", - FormatterNamespace = namespaceDot + "Formatters", - ResolverName = resolverName, - RegisterInfos = genericInfo.Where(x => !x.IsOpenGenericType).Cast().Concat(enumInfo).Concat(unionInfo).Concat(objectInfo.Where(x => !x.IsOpenGenericType)).ToArray(), - }; - - await OutputToDirAsync(output, resolverTemplate.Namespace, resolverTemplate.ResolverName, multioutSymbol, resolverTemplate.TransformText(), cancellationToken); + await GenerateMultipleFileAsync(output, resolverName, objectInfo, enumInfo, unionInfo, namespaceDot, multioutSymbol, genericInfo); } if (objectInfo.Length == 0 && enumInfo.Length == 0 && genericInfo.Length == 0 && unionInfo.Length == 0) @@ -205,6 +100,134 @@ public async Task GenerateFileAsync( logger("Output Generation Complete:" + sw.Elapsed.ToString()); } + /// + /// Generates the specialized resolver and formatters for the types that require serialization in a given compilation. + /// + /// The resolver name. + /// The namespace for the generated type to be created in. + /// The ObjectSerializationInfo array which TypeCollector.Collect returns. + /// The EnumSerializationInfo array which TypeCollector.Collect returns. + /// The UnionSerializationInfo array which TypeCollector.Collect returns. + /// The GenericSerializationInfo array which TypeCollector.Collect returns. + 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 template = isStringKey ? new StringKeyFormatterTemplate() : (IFormatterTemplate)new FormatterTemplate(); + + template.Namespace = namespaceDot + "Formatters" + (nameSpace is null ? string.Empty : "." + nameSpace); + template.ObjectSerializationInfos = objectSerializationInfos; + + return template; + }) + .ToArray(); + + 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(); + + var resolverTemplate = new ResolverTemplate() + { + Namespace = namespaceDot + "Resolvers", + FormatterNamespace = namespaceDot + "Formatters", + ResolverName = resolverName, + RegisterInfos = genericInfo.Where(x => !x.IsOpenGenericType).Cast().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) + { + var waitingTasks = new Task[objectInfo.Length + enumInfo.Length + unionInfo.Length + 1]; + var waitingIndex = 0; + 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(); + waitingTasks[waitingIndex++] = OutputToDirAsync(output, template.Namespace, x.Name + "Formatter", multioutSymbol, text, cancellationToken); + } + + foreach (var x in enumInfo) + { + var template = new EnumTemplate() + { + Namespace = namespaceDot + "Formatters" + ((x.Namespace == null) ? string.Empty : "." + x.Namespace), + EnumSerializationInfos = new[] { x }, + }; + + var text = template.TransformText(); + waitingTasks[waitingIndex++] = OutputToDirAsync(output, template.Namespace, x.Name + "Formatter", multioutSymbol, text, cancellationToken); + } + + foreach (var x in unionInfo) + { + var template = new UnionTemplate() + { + Namespace = namespaceDot + "Formatters" + ((x.Namespace == null) ? string.Empty : "." + x.Namespace), + UnionSerializationInfos = new[] { x }, + }; + + var text = template.TransformText(); + waitingTasks[waitingIndex++] = OutputToDirAsync(output, template.Namespace, x.Name + "Formatter", multioutSymbol, text, cancellationToken); + } + + var resolverTemplate = new ResolverTemplate() + { + Namespace = namespaceDot + "Resolvers", + FormatterNamespace = namespaceDot + "Formatters", + ResolverName = resolverName, + RegisterInfos = genericInfo.Where(x => !x.IsOpenGenericType).Cast().Concat(enumInfo).Concat(unionInfo).Concat(objectInfo.Where(x => !x.IsOpenGenericType)).ToArray(), + }; + + waitingTasks[waitingIndex] = OutputToDirAsync(output, resolverTemplate.Namespace, resolverTemplate.ResolverName, multioutSymbol, resolverTemplate.TransformText(), cancellationToken); + return Task.WhenAll(waitingTasks); + } + private Task OutputToDirAsync(string dir, string ns, string name, string multipleOutSymbol, string text, CancellationToken cancellationToken) { if (multipleOutSymbol == string.Empty)