From a8eba9573a4b62f829324764bfb8133ea0f30672 Mon Sep 17 00:00:00 2001
From: y-sasaki <11359025+Y-YoL@users.noreply.github.com>
Date: Fri, 2 Dec 2022 21:39:51 +0900
Subject: [PATCH 001/660] Change globa.json rollForward to feature
---
global.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/global.json b/global.json
index 954a92e75..9d800d823 100644
--- a/global.json
+++ b/global.json
@@ -1,7 +1,7 @@
{
"sdk": {
"version": "6.0.300",
- "rollForward": "patch",
+ "rollForward": "feature",
"allowPrerelease": false
}
}
From fe3c6e5340fbcf076266abd0d3c5286c92c82645 Mon Sep 17 00:00:00 2001
From: y-sasaki <11359025+Y-YoL@users.noreply.github.com>
Date: Mon, 19 Dec 2022 20:58:12 +0900
Subject: [PATCH 002/660] Change generator project setting
- remove project dependency
- update dependency package version
---
.../MessagePack.Generator.csproj | 86 ++++++++++++++++---
1 file changed, 74 insertions(+), 12 deletions(-)
diff --git a/src/MessagePack.Generator/MessagePack.Generator.csproj b/src/MessagePack.Generator/MessagePack.Generator.csproj
index 42b02f30f..d5bb59495 100644
--- a/src/MessagePack.Generator/MessagePack.Generator.csproj
+++ b/src/MessagePack.Generator/MessagePack.Generator.csproj
@@ -3,13 +3,12 @@
mpc
Exe
- netcoreapp3.1;net6.0
- 10
+ netstandard2.0
+ 11
+ enable
enable
- true
- true
- mpc
- Major
+ true
+ cs
MessagePack.Generator
@@ -19,15 +18,78 @@
-
-
-
-
-
+
+
-
+
+
+
+
+
+
+ True
+ True
+ EnumTemplate.tt
+
+
+ True
+ True
+ FormatterTemplate.tt
+
+
+ True
+ True
+ ResolverTemplate.tt
+
+
+ True
+ True
+ StringKeyFormatterTemplate.tt
+
+
+ %(FileName).tt
+ True
+ True
+
+
+ True
+ True
+ UnionTemplate.tt
+
+
+
+
+
+ TextTemplatingFilePreprocessor
+ EnumTemplate.cs
+ MessagePackCompiler.Generator
+
+
+ TextTemplatingFilePreprocessor
+ FormatterTemplate.cs
+ MessagePackCompiler.Generator
+
+
+ TextTemplatingFilePreprocessor
+ ResolverTemplate.cs
+ MessagePackCompiler.Generator
+
+
+ TextTemplatingFilePreprocessor
+ StringKeyFormatterTemplate.cs
+ MessagePackCompiler.Generator
+
+
+ TextTemplatingFilePreprocessor
+ UnionTemplate.cs
+ MessagePackCompiler.Generator
+
+
+
+
+
From 539f7d7679318f998114d050d2c5eb746e6b96d9 Mon Sep 17 00:00:00 2001
From: y-sasaki <11359025+Y-YoL@users.noreply.github.com>
Date: Mon, 19 Dec 2022 21:35:18 +0900
Subject: [PATCH 003/660] Add dependency reference
---
src/MessagePack.Generator/MessagePack.Generator.csproj | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/src/MessagePack.Generator/MessagePack.Generator.csproj b/src/MessagePack.Generator/MessagePack.Generator.csproj
index d5bb59495..e97461f3e 100644
--- a/src/MessagePack.Generator/MessagePack.Generator.csproj
+++ b/src/MessagePack.Generator/MessagePack.Generator.csproj
@@ -1,8 +1,6 @@
- mpc
- Exe
netstandard2.0
11
enable
@@ -20,11 +18,16 @@
+
+
+
+
+
From ceefa17010a1397ccbedd8f9c1dfcd0efd24fdf2 Mon Sep 17 00:00:00 2001
From: y-sasaki <11359025+Y-YoL@users.noreply.github.com>
Date: Mon, 19 Dec 2022 21:38:23 +0900
Subject: [PATCH 004/660] Replace Generator
---
.../IGeneratorContext.cs | 11 ++
.../MessagepackCompiler.cs | 108 ---------------
.../MessagepackGenerator.Emit.cs | 108 +++++++++++++++
.../MessagepackGenerator.cs | 128 ++++++++++++++++++
.../CodeAnalysis/TypeCollector.cs | 35 ++++-
5 files changed, 280 insertions(+), 110 deletions(-)
create mode 100644 src/MessagePack.Generator/IGeneratorContext.cs
delete mode 100644 src/MessagePack.Generator/MessagepackCompiler.cs
create mode 100644 src/MessagePack.Generator/MessagepackGenerator.Emit.cs
create mode 100644 src/MessagePack.Generator/MessagepackGenerator.cs
diff --git a/src/MessagePack.Generator/IGeneratorContext.cs b/src/MessagePack.Generator/IGeneratorContext.cs
new file mode 100644
index 000000000..b4fe9f3a8
--- /dev/null
+++ b/src/MessagePack.Generator/IGeneratorContext.cs
@@ -0,0 +1,11 @@
+// Copyright (c) All contributors. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+namespace MessagePack.Generator;
+
+public interface IGeneratorContext
+{
+ CancellationToken CancellationToken { get; }
+
+ void AddSource(string hintName, string source);
+}
diff --git a/src/MessagePack.Generator/MessagepackCompiler.cs b/src/MessagePack.Generator/MessagepackCompiler.cs
deleted file mode 100644
index dfea81961..000000000
--- a/src/MessagePack.Generator/MessagepackCompiler.cs
+++ /dev/null
@@ -1,108 +0,0 @@
-// Copyright (c) All contributors. All rights reserved.
-// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Runtime.Loader;
-using System.Threading;
-using System.Threading.Tasks;
-using ConsoleAppFramework;
-using Microsoft.Build.Locator;
-using Microsoft.Build.Logging;
-using Microsoft.CodeAnalysis;
-using Microsoft.CodeAnalysis.CSharp;
-using Microsoft.CodeAnalysis.MSBuild;
-using Microsoft.Extensions.Hosting;
-
-namespace MessagePack.Generator
-{
- public class MessagepackCompiler : ConsoleAppBase
- {
- private static async Task Main(string[] args)
- {
- var instance = MSBuildLocator.RegisterDefaults();
- AssemblyLoadContext.Default.Resolving += (assemblyLoadContext, assemblyName) =>
- {
- var path = Path.Combine(instance.MSBuildPath, assemblyName.Name + ".dll");
- if (File.Exists(path))
- {
- return assemblyLoadContext.LoadFromAssemblyPath(path);
- }
-
- return null;
- };
-
- await Host.CreateDefaultBuilder()
- .ConfigureLogging(logging => logging.ReplaceToSimpleConsole())
- .RunConsoleAppFrameworkAsync(args);
- }
-
- public async Task RunAsync(
- [Option("i", "Input path to MSBuild project file or the directory containing Unity source files.")] string input,
- [Option("o", "Output file path(.cs) or directory (multiple generate file).")] string output,
- [Option("c", "Conditional compiler symbols, split with ','. Ignored if a project file is specified for input.")] string? conditionalSymbol = null,
- [Option("r", "Set resolver name.")] string resolverName = "GeneratedResolver",
- [Option("n", "Set namespace root name.")] string @namespace = "MessagePack",
- [Option("m", "Force use map mode serialization.")] bool useMapMode = false,
- [Option("ms", "Generate #if-- files by symbols, split with ','.")] string? multipleIfDirectiveOutputSymbols = null,
- [Option("ei", "Ignore type names.")] string[]? externalIgnoreTypeNames = null)
- {
- Workspace? workspace = null;
- try
- {
- Compilation compilation;
- if (Directory.Exists(input))
- {
- string[]? conditionalSymbols = conditionalSymbol?.Split(',');
- compilation = await PseudoCompilation.CreateFromDirectoryAsync(input, conditionalSymbols, this.Context.CancellationToken);
- }
- else
- {
- (workspace, compilation) = await this.OpenMSBuildProjectAsync(input, this.Context.CancellationToken);
- }
-
- await new MessagePackCompiler.CodeGenerator(x => Console.WriteLine(x), this.Context.CancellationToken)
- .GenerateFileAsync(
- compilation,
- output,
- resolverName,
- @namespace,
- useMapMode,
- multipleIfDirectiveOutputSymbols,
- externalIgnoreTypeNames).ConfigureAwait(false);
- }
- catch (OperationCanceledException)
- {
- await Console.Error.WriteLineAsync("Canceled");
- throw;
- }
- finally
- {
- workspace?.Dispose();
- }
- }
-
- private async Task<(Workspace Workspace, Compilation Compilation)> OpenMSBuildProjectAsync(string projectPath, CancellationToken cancellationToken)
- {
- var workspace = MSBuildWorkspace.Create();
- try
- {
- var logger = new ConsoleLogger(Microsoft.Build.Framework.LoggerVerbosity.Quiet);
- var project = await workspace.OpenProjectAsync(projectPath, logger, null, cancellationToken);
- var compilation = await project.GetCompilationAsync(cancellationToken);
- if (compilation is null)
- {
- throw new NotSupportedException("The project does not support creating Compilation.");
- }
-
- return (workspace, compilation);
- }
- catch
- {
- workspace.Dispose();
- throw;
- }
- }
- }
-}
diff --git a/src/MessagePack.Generator/MessagepackGenerator.Emit.cs b/src/MessagePack.Generator/MessagepackGenerator.Emit.cs
new file mode 100644
index 000000000..9056748f7
--- /dev/null
+++ b/src/MessagePack.Generator/MessagepackGenerator.Emit.cs
@@ -0,0 +1,108 @@
+// Copyright (c) All contributors. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System.Text;
+using MessagePackCompiler.CodeAnalysis;
+using MessagePackCompiler.Generator;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+
+namespace MessagePack.Generator;
+
+public partial class MessagepackGenerator
+{
+ private static void Generate(TypeDeclarationSyntax syntax, Compilation compilation, IGeneratorContext context)
+ {
+ var semanticModel = compilation.GetSemanticModel(syntax.SyntaxTree);
+
+ var typeSymbol = semanticModel.GetDeclaredSymbol(syntax, context.CancellationToken) as ITypeSymbol;
+ if (typeSymbol == null)
+ {
+ return;
+ }
+
+ var fullType = typeSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)
+ .Replace("global::", string.Empty)
+ .Replace("<", "_")
+ .Replace(">", "_");
+
+ var collector = new TypeCollector(compilation, true, isForceUseMap: false, ignoreTypeNames: null, typeSymbol);
+
+ var (objectInfo, enumInfo, genericInfo, unionInfo) = collector.Collect();
+
+ var code = GenerateFormatterSync(string.Empty, string.Empty, objectInfo, enumInfo, unionInfo, genericInfo);
+
+ context.AddSource($"{fullType}.MessagePackFormatter.g.cs", code);
+ }
+
+ ///
+ /// 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.
+ private static string GenerateFormatterSync(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(IGrouping x)
+ {
+ if (x.Key == null)
+ {
+ return namespaceDot + "Formatters";
+ }
+
+ return namespaceDot + "Formatters." + x.Key;
+ }
+
+ var enumFormatterTemplates = enumInfo
+ .GroupBy(x => x.Namespace)
+ .Select(x => new EnumTemplate(GetNamespace(x), x.ToArray()))
+ .ToArray();
+
+ var unionFormatterTemplates = unionInfo
+ .GroupBy(x => x.Namespace)
+ .Select(x => new UnionTemplate(GetNamespace(x), x.ToArray()))
+ .ToArray();
+
+ var resolverTemplate = new ResolverTemplate(namespaceDot + "Resolvers", namespaceDot + "Formatters", resolverName, 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();
+ }
+}
diff --git a/src/MessagePack.Generator/MessagepackGenerator.cs b/src/MessagePack.Generator/MessagepackGenerator.cs
new file mode 100644
index 000000000..d5ff98aed
--- /dev/null
+++ b/src/MessagePack.Generator/MessagepackGenerator.cs
@@ -0,0 +1,128 @@
+// Copyright (c) All contributors. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+
+namespace MessagePack.Generator;
+
+[Generator(LanguageNames.CSharp)]
+public partial class MessagepackGenerator : IIncrementalGenerator
+{
+ public const string MessagePackObjectAttributeFullName = "MessagePack.MessagePackObjectAttribute";
+
+ public void Initialize(IncrementalGeneratorInitializationContext context)
+ {
+ var typeDeclarations = context.SyntaxProvider.ForAttributeWithMetadataName(
+ MessagePackObjectAttributeFullName,
+ predicate: static (node, _) => node is TypeDeclarationSyntax,
+ transform: static (context, _) => (TypeDeclarationSyntax)context.TargetNode);
+
+ var source = typeDeclarations
+ .Combine(context.CompilationProvider)
+ .WithComparer(Comparer.Instance);
+
+ context.RegisterSourceOutput(source, static (context, source) =>
+ {
+ var (typeDeclaration, compilation) = source;
+ Generate(typeDeclaration, compilation, new GeneratorContext(context));
+ });
+ }
+
+ private class Comparer : IEqualityComparer<(TypeDeclarationSyntax, Compilation)>
+ {
+ public static readonly Comparer Instance = new Comparer();
+
+ public bool Equals((TypeDeclarationSyntax, Compilation) x, (TypeDeclarationSyntax, Compilation) y)
+ {
+ return x.Item1.Equals(y.Item1);
+ }
+
+ public int GetHashCode((TypeDeclarationSyntax, Compilation) obj)
+ {
+ return obj.Item1.GetHashCode();
+ }
+ }
+
+ private class GeneratorContext : IGeneratorContext
+ {
+ private SourceProductionContext context;
+
+ public GeneratorContext(SourceProductionContext context)
+ {
+ this.context = context;
+ }
+
+ public CancellationToken CancellationToken => context.CancellationToken;
+
+ public void AddSource(string hintName, string source) => context.AddSource(hintName, source);
+ }
+
+#if false
+ public async Task RunAsync(
+ [Option("i", "Input path to MSBuild project file or the directory containing Unity source files.")] string input,
+ [Option("o", "Output file path(.cs) or directory (multiple generate file).")] string output,
+ [Option("c", "Conditional compiler symbols, split with ','. Ignored if a project file is specified for input.")] string? conditionalSymbol = null,
+ [Option("r", "Set resolver name.")] string resolverName = "GeneratedResolver",
+ [Option("n", "Set namespace root name.")] string @namespace = "MessagePack",
+ [Option("m", "Force use map mode serialization.")] bool useMapMode = false,
+ [Option("ms", "Generate #if-- files by symbols, split with ','.")] string? multipleIfDirectiveOutputSymbols = null,
+ [Option("ei", "Ignore type names.")] string[]? externalIgnoreTypeNames = null)
+ {
+ try
+ {
+ Compilation compilation;
+ if (Directory.Exists(input))
+ {
+ string[]? conditionalSymbols = conditionalSymbol?.Split(',');
+ compilation = await PseudoCompilation.CreateFromDirectoryAsync(input, conditionalSymbols, this.Context.CancellationToken);
+ }
+ else
+ {
+ (workspace, compilation) = await this.OpenMSBuildProjectAsync(input, this.Context.CancellationToken);
+ }
+
+ await new MessagePackCompiler.CodeGenerator(x => Console.WriteLine(x), this.Context.CancellationToken)
+ .GenerateFileAsync(
+ compilation,
+ output,
+ resolverName,
+ @namespace,
+ useMapMode,
+ multipleIfDirectiveOutputSymbols,
+ externalIgnoreTypeNames).ConfigureAwait(false);
+ }
+ catch (OperationCanceledException)
+ {
+ await Console.Error.WriteLineAsync("Canceled");
+ throw;
+ }
+ finally
+ {
+ workspace?.Dispose();
+ }
+ }
+
+ private async Task<(Workspace Workspace, Compilation Compilation)> OpenMSBuildProjectAsync(string projectPath, CancellationToken cancellationToken)
+ {
+ var workspace = MSBuildWorkspace.Create();
+ try
+ {
+ var logger = new ConsoleLogger(Microsoft.Build.Framework.LoggerVerbosity.Quiet);
+ var project = await workspace.OpenProjectAsync(projectPath, logger, null, cancellationToken);
+ var compilation = await project.GetCompilationAsync(cancellationToken);
+ if (compilation is null)
+ {
+ throw new NotSupportedException("The project does not support creating Compilation.");
+ }
+
+ return (workspace, compilation);
+ }
+ catch
+ {
+ workspace.Dispose();
+ throw;
+ }
+ }
+#endif
+}
diff --git a/src/MessagePack.GeneratorCore/CodeAnalysis/TypeCollector.cs b/src/MessagePack.GeneratorCore/CodeAnalysis/TypeCollector.cs
index da68130aa..9f7fd8c0f 100644
--- a/src/MessagePack.GeneratorCore/CodeAnalysis/TypeCollector.cs
+++ b/src/MessagePack.GeneratorCore/CodeAnalysis/TypeCollector.cs
@@ -91,7 +91,7 @@ public class TypeCollector
private readonly bool isForceUseMap;
private readonly ReferenceSymbols typeReferences;
- private readonly INamedTypeSymbol[] targetTypes;
+ private readonly ITypeSymbol[] targetTypes;
private readonly HashSet embeddedTypes = new(new[]
{
"short",
@@ -285,6 +285,37 @@ public TypeCollector(Compilation compilation, bool disallowInternal, bool isForc
.ToArray();
}
+ public TypeCollector(Compilation compilation, bool disallowInternal, bool isForceUseMap, string[]? ignoreTypeNames, ITypeSymbol targetType)
+ {
+ this.typeReferences = new ReferenceSymbols(compilation, _ => { });
+ this.disallowInternal = disallowInternal;
+ this.isForceUseMap = isForceUseMap;
+ this.externalIgnoreTypeNames = new HashSet(ignoreTypeNames ?? Array.Empty());
+ this.compilation = compilation;
+
+ targetTypes = new[] { targetType }
+ .Where(x =>
+ {
+ if (x.DeclaredAccessibility == Accessibility.Public)
+ {
+ return true;
+ }
+
+ if (!disallowInternal)
+ {
+ return x.DeclaredAccessibility == Accessibility.Friend;
+ }
+
+ return false;
+ })
+ .Where(x =>
+ ((x.TypeKind == TypeKind.Interface) && x.GetAttributes().Any(x2 => x2.AttributeClass.ApproximatelyEqual(typeReferences.UnionAttribute)))
+ || ((x.TypeKind == TypeKind.Class && x.IsAbstract) && x.GetAttributes().Any(x2 => x2.AttributeClass.ApproximatelyEqual(typeReferences.UnionAttribute)))
+ || ((x.TypeKind == TypeKind.Class) && x.GetAttributes().Any(x2 => x2.AttributeClass.ApproximatelyEqual(typeReferences.MessagePackObjectAttribute)))
+ || ((x.TypeKind == TypeKind.Struct) && x.GetAttributes().Any(x2 => x2.AttributeClass.ApproximatelyEqual(typeReferences.MessagePackObjectAttribute))))
+ .ToArray();
+ }
+
private void ResetWorkspace()
{
this.alreadyCollected.Clear();
@@ -299,7 +330,7 @@ private void ResetWorkspace()
{
this.ResetWorkspace();
- foreach (INamedTypeSymbol item in this.targetTypes)
+ foreach (var item in this.targetTypes)
{
this.CollectCore(item);
}
From 9f695cc822420fa45e3c453a8b97c83aa888a705 Mon Sep 17 00:00:00 2001
From: y-sasaki <11359025+Y-YoL@users.noreply.github.com>
Date: Mon, 19 Dec 2022 23:37:45 +0900
Subject: [PATCH 005/660] Replace GeneratedResolver to ModuleInitializer
---
.../MessagepackGenerator.Emit.cs | 41 +++++++++++++++++--
.../Resolvers/StaticCompositeResolver.cs | 21 ++++++++++
2 files changed, 58 insertions(+), 4 deletions(-)
diff --git a/src/MessagePack.Generator/MessagepackGenerator.Emit.cs b/src/MessagePack.Generator/MessagepackGenerator.Emit.cs
index 9056748f7..921521551 100644
--- a/src/MessagePack.Generator/MessagepackGenerator.Emit.cs
+++ b/src/MessagePack.Generator/MessagepackGenerator.Emit.cs
@@ -78,14 +78,11 @@ string GetNamespace(IGrouping x)
.Select(x => new UnionTemplate(GetNamespace(x), x.ToArray()))
.ToArray();
- var resolverTemplate = new ResolverTemplate(namespaceDot + "Resolvers", namespaceDot + "Formatters", resolverName, 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();
+ ResolverText(sb, item.Namespace, item.EnumSerializationInfos.Select(x => x.Name));
sb.AppendLine(text);
}
@@ -93,6 +90,7 @@ string GetNamespace(IGrouping x)
foreach (var item in unionFormatterTemplates)
{
var text = item.TransformText();
+ ResolverText(sb, item.Namespace, item.UnionSerializationInfos.Select(x => x.Name));
sb.AppendLine(text);
}
@@ -100,9 +98,44 @@ string GetNamespace(IGrouping x)
foreach (var item in objectFormatterTemplates)
{
var text = item.TransformText();
+ ResolverText(sb, item.Namespace, item.ObjectSerializationInfos.Select(x => x.Name));
sb.AppendLine(text);
}
return sb.ToString();
}
+
+ private static void ResolverText(StringBuilder sb, string ns, IEnumerable names)
+ {
+ var begin = $$"""
+using System.Runtime.CompilerServices;
+
+namespace {{ns}}
+{
+ partial class FormatterRegister
+ {
+""";
+
+ var end = $$"""
+ }
+}
+""";
+
+ sb.AppendLine(begin);
+
+ foreach (var item in names)
+ {
+ var code = $$"""
+
+ [ModuleInitializer]
+ internal static void {{item}}FormatterRegister()
+ {
+ MessagePack.Resolvers.StaticCompositeResolver.Instance.AddGeneratedFormatter(new global::{{ns}}.{{item}}Formatter());
+ }
+""";
+ sb.AppendLine(code);
+ }
+
+ sb.AppendLine(end);
+ }
}
diff --git a/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Resolvers/StaticCompositeResolver.cs b/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Resolvers/StaticCompositeResolver.cs
index 8d8037bc2..441eff7f2 100644
--- a/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Resolvers/StaticCompositeResolver.cs
+++ b/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Resolvers/StaticCompositeResolver.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.Concurrent;
using System.Collections.Generic;
using MessagePack.Formatters;
@@ -15,6 +16,7 @@ public class StaticCompositeResolver : IFormatterResolver
public static readonly StaticCompositeResolver Instance = new StaticCompositeResolver();
private bool freezed;
+ private ConcurrentBag generatedFormatters = new ConcurrentBag();
private IReadOnlyList formatters;
private IReadOnlyList resolvers;
@@ -108,6 +110,16 @@ public void Register(IReadOnlyList formatters, IReadOnlyL
this.resolvers = resolvers;
}
+ public void AddGeneratedFormatter(IMessagePackFormatter formatter)
+ {
+ if (this.freezed)
+ {
+ throw new InvalidOperationException("Register must call on startup(before use GetFormatter).");
+ }
+
+ this.generatedFormatters.Add(formatter);
+ }
+
///
/// Gets an instance that can serialize or deserialize some type .
///
@@ -125,6 +137,15 @@ private static class Cache
static Cache()
{
Instance.freezed = true;
+ foreach (var item in Instance.generatedFormatters)
+ {
+ if (item is IMessagePackFormatter f)
+ {
+ Formatter = f;
+ return;
+ }
+ }
+
foreach (var item in Instance.formatters)
{
if (item is IMessagePackFormatter f)
From 4db43421949fe26f779347b6ce1c20f1a3af9406 Mon Sep 17 00:00:00 2001
From: y-sasaki <11359025+Y-YoL@users.noreply.github.com>
Date: Thu, 22 Dec 2022 21:40:26 +0900
Subject: [PATCH 006/660] Add Generator.Roslyn3 csproj
---
MessagePack.sln | 11 +-
.../.editorconfig | 4 +
.../MessagePack.Generator.Roslyn3.csproj | 103 ++++++++
.../MessagepackGenerator.cs | 82 ++++++
.../MessagepackGenerator.cs | 68 -----
.../PseudoCompilation.cs | 235 ------------------
6 files changed, 198 insertions(+), 305 deletions(-)
create mode 100644 src/MessagePack.Generator.Roslyn3/.editorconfig
create mode 100644 src/MessagePack.Generator.Roslyn3/MessagePack.Generator.Roslyn3.csproj
create mode 100644 src/MessagePack.Generator.Roslyn3/MessagepackGenerator.cs
delete mode 100644 src/MessagePack.Generator/PseudoCompilation.cs
diff --git a/MessagePack.sln b/MessagePack.sln
index 77d762b8a..d13f47aa6 100644
--- a/MessagePack.sln
+++ b/MessagePack.sln
@@ -1,7 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 16
-VisualStudioVersion = 16.0.29108.181
+# Visual Studio Version 17
+VisualStudioVersion = 17.4.33103.184
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{86309CF6-0054-4CE3-BFD3-CA0AA7DB17BC}"
EndProject
@@ -88,6 +88,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MessagePack.Experimental.Te
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MessagePack.GeneratedCode.Tests", "tests\MessagePack.GeneratedCode.Tests\MessagePack.GeneratedCode.Tests.csproj", "{D4CE7347-CEBE-46E5-BD12-1319573B6C5E}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MessagePack.Generator.Roslyn3", "src\MessagePack.Generator.Roslyn3\MessagePack.Generator.Roslyn3.csproj", "{45A72780-93EF-4CD1-9FCD-D56A42A3B966}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -202,6 +204,10 @@ Global
{D4CE7347-CEBE-46E5-BD12-1319573B6C5E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D4CE7347-CEBE-46E5-BD12-1319573B6C5E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D4CE7347-CEBE-46E5-BD12-1319573B6C5E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {45A72780-93EF-4CD1-9FCD-D56A42A3B966}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {45A72780-93EF-4CD1-9FCD-D56A42A3B966}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {45A72780-93EF-4CD1-9FCD-D56A42A3B966}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {45A72780-93EF-4CD1-9FCD-D56A42A3B966}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -234,6 +240,7 @@ Global
{AC2503A7-736D-4AE6-9355-CF35D9DF6139} = {86309CF6-0054-4CE3-BFD3-CA0AA7DB17BC}
{8AB40D1C-1134-4D77-B39A-19AEDC729450} = {19FE674A-AC94-4E7E-B24C-2285D1D04CDE}
{D4CE7347-CEBE-46E5-BD12-1319573B6C5E} = {19FE674A-AC94-4E7E-B24C-2285D1D04CDE}
+ {45A72780-93EF-4CD1-9FCD-D56A42A3B966} = {86309CF6-0054-4CE3-BFD3-CA0AA7DB17BC}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {B3911209-2DBF-47F8-98F6-BBC0EDFE63DE}
diff --git a/src/MessagePack.Generator.Roslyn3/.editorconfig b/src/MessagePack.Generator.Roslyn3/.editorconfig
new file mode 100644
index 000000000..6b835fc65
--- /dev/null
+++ b/src/MessagePack.Generator.Roslyn3/.editorconfig
@@ -0,0 +1,4 @@
+[*.cs]
+
+# VSTHRD111: Use ConfigureAwait(bool)
+dotnet_diagnostic.VSTHRD111.severity = none
diff --git a/src/MessagePack.Generator.Roslyn3/MessagePack.Generator.Roslyn3.csproj b/src/MessagePack.Generator.Roslyn3/MessagePack.Generator.Roslyn3.csproj
new file mode 100644
index 000000000..e6138e486
--- /dev/null
+++ b/src/MessagePack.Generator.Roslyn3/MessagePack.Generator.Roslyn3.csproj
@@ -0,0 +1,103 @@
+
+
+
+ netstandard2.0
+ 11
+ enable
+ enable
+ ROSLYN3
+ true
+ cs
+
+
+ MessagePack.Generator
+ MessagePack Code Generator
+ MessagePack standalone code generator.
+ MsgPack;MessagePack;Serialization;Formatter;Serializer;Unity;Xamarin
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ True
+ True
+ EnumTemplate.tt
+
+
+ True
+ True
+ FormatterTemplate.tt
+
+
+ True
+ True
+ ResolverTemplate.tt
+
+
+ True
+ True
+ StringKeyFormatterTemplate.tt
+
+
+ %(FileName).tt
+ True
+ True
+
+
+ True
+ True
+ UnionTemplate.tt
+
+
+
+
+
+ TextTemplatingFilePreprocessor
+ EnumTemplate.cs
+ MessagePackCompiler.Generator
+
+
+ TextTemplatingFilePreprocessor
+ FormatterTemplate.cs
+ MessagePackCompiler.Generator
+
+
+ TextTemplatingFilePreprocessor
+ ResolverTemplate.cs
+ MessagePackCompiler.Generator
+
+
+ TextTemplatingFilePreprocessor
+ StringKeyFormatterTemplate.cs
+ MessagePackCompiler.Generator
+
+
+ TextTemplatingFilePreprocessor
+ UnionTemplate.cs
+ MessagePackCompiler.Generator
+
+
+
+
+
+
+
+
diff --git a/src/MessagePack.Generator.Roslyn3/MessagepackGenerator.cs b/src/MessagePack.Generator.Roslyn3/MessagepackGenerator.cs
new file mode 100644
index 000000000..316f914b3
--- /dev/null
+++ b/src/MessagePack.Generator.Roslyn3/MessagepackGenerator.cs
@@ -0,0 +1,82 @@
+// Copyright (c) All contributors. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+
+namespace MessagePack.Generator;
+
+[Generator(LanguageNames.CSharp)]
+public partial class MessagepackGenerator : ISourceGenerator
+{
+ public const string MessagePackObjectAttributeFullName = "MessagePack.MessagePackObjectAttribute";
+
+ public void Initialize(GeneratorInitializationContext context)
+ {
+ context.RegisterForSyntaxNotifications(SyntaxContextReceiver.Create);
+ }
+
+ public void Execute(GeneratorExecutionContext context)
+ {
+ if (context.SyntaxContextReceiver is not SyntaxContextReceiver receiver || receiver.ClassDeclarations.Count == 0)
+ {
+ return;
+ }
+
+ var compiation = context.Compilation;
+ var generateContext = new GeneratorContext(context);
+
+ foreach (var syntax in receiver.ClassDeclarations)
+ {
+ Generate(syntax, compiation, generateContext);
+ }
+ }
+
+ private class SyntaxContextReceiver : ISyntaxContextReceiver
+ {
+ internal static ISyntaxContextReceiver Create()
+ {
+ return new SyntaxContextReceiver();
+ }
+
+ public HashSet ClassDeclarations { get; } = new();
+
+ public void OnVisitSyntaxNode(GeneratorSyntaxContext context)
+ {
+ if (context.Node is TypeDeclarationSyntax typeSyntax)
+ {
+ if (typeSyntax.AttributeLists.Count > 0)
+ {
+ var hasAttribute = typeSyntax.AttributeLists
+ .SelectMany(x => x.Attributes)
+ .Any(x => x.Name.ToString() is "MessagePackObject"
+ or "MessagePackObjectAttribute"
+ or "MessagePack.MessagePackObject"
+ or "MessagePack.MessagePackObjectAttribute"
+ or "Union"
+ or "UnionAttribute"
+ or "MessagePack.Union"
+ or "MessagePack.UnionAttribute");
+ if (hasAttribute)
+ {
+ ClassDeclarations.Add(typeSyntax);
+ }
+ }
+ }
+ }
+ }
+
+ private class GeneratorContext : IGeneratorContext
+ {
+ private GeneratorExecutionContext context;
+
+ public GeneratorContext(GeneratorExecutionContext context)
+ {
+ this.context = context;
+ }
+
+ public CancellationToken CancellationToken => context.CancellationToken;
+
+ public void AddSource(string hintName, string source) => context.AddSource(hintName, source);
+ }
+}
diff --git a/src/MessagePack.Generator/MessagepackGenerator.cs b/src/MessagePack.Generator/MessagepackGenerator.cs
index d5ff98aed..c5344dcf5 100644
--- a/src/MessagePack.Generator/MessagepackGenerator.cs
+++ b/src/MessagePack.Generator/MessagepackGenerator.cs
@@ -57,72 +57,4 @@ public GeneratorContext(SourceProductionContext context)
public void AddSource(string hintName, string source) => context.AddSource(hintName, source);
}
-
-#if false
- public async Task RunAsync(
- [Option("i", "Input path to MSBuild project file or the directory containing Unity source files.")] string input,
- [Option("o", "Output file path(.cs) or directory (multiple generate file).")] string output,
- [Option("c", "Conditional compiler symbols, split with ','. Ignored if a project file is specified for input.")] string? conditionalSymbol = null,
- [Option("r", "Set resolver name.")] string resolverName = "GeneratedResolver",
- [Option("n", "Set namespace root name.")] string @namespace = "MessagePack",
- [Option("m", "Force use map mode serialization.")] bool useMapMode = false,
- [Option("ms", "Generate #if-- files by symbols, split with ','.")] string? multipleIfDirectiveOutputSymbols = null,
- [Option("ei", "Ignore type names.")] string[]? externalIgnoreTypeNames = null)
- {
- try
- {
- Compilation compilation;
- if (Directory.Exists(input))
- {
- string[]? conditionalSymbols = conditionalSymbol?.Split(',');
- compilation = await PseudoCompilation.CreateFromDirectoryAsync(input, conditionalSymbols, this.Context.CancellationToken);
- }
- else
- {
- (workspace, compilation) = await this.OpenMSBuildProjectAsync(input, this.Context.CancellationToken);
- }
-
- await new MessagePackCompiler.CodeGenerator(x => Console.WriteLine(x), this.Context.CancellationToken)
- .GenerateFileAsync(
- compilation,
- output,
- resolverName,
- @namespace,
- useMapMode,
- multipleIfDirectiveOutputSymbols,
- externalIgnoreTypeNames).ConfigureAwait(false);
- }
- catch (OperationCanceledException)
- {
- await Console.Error.WriteLineAsync("Canceled");
- throw;
- }
- finally
- {
- workspace?.Dispose();
- }
- }
-
- private async Task<(Workspace Workspace, Compilation Compilation)> OpenMSBuildProjectAsync(string projectPath, CancellationToken cancellationToken)
- {
- var workspace = MSBuildWorkspace.Create();
- try
- {
- var logger = new ConsoleLogger(Microsoft.Build.Framework.LoggerVerbosity.Quiet);
- var project = await workspace.OpenProjectAsync(projectPath, logger, null, cancellationToken);
- var compilation = await project.GetCompilationAsync(cancellationToken);
- if (compilation is null)
- {
- throw new NotSupportedException("The project does not support creating Compilation.");
- }
-
- return (workspace, compilation);
- }
- catch
- {
- workspace.Dispose();
- throw;
- }
- }
-#endif
}
diff --git a/src/MessagePack.Generator/PseudoCompilation.cs b/src/MessagePack.Generator/PseudoCompilation.cs
deleted file mode 100644
index 80d5a1ed2..000000000
--- a/src/MessagePack.Generator/PseudoCompilation.cs
+++ /dev/null
@@ -1,235 +0,0 @@
-// Copyright (c) All contributors. All rights reserved.
-// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Runtime.Serialization;
-using System.Text;
-using System.Threading;
-using System.Threading.Tasks;
-using Microsoft.CodeAnalysis;
-using Microsoft.CodeAnalysis.CSharp;
-using Microsoft.CodeAnalysis.CSharp.Syntax;
-
-namespace MessagePack.Generator
-{
- internal static class PseudoCompilation
- {
- internal static async Task CreateFromDirectoryAsync(string directoryRoot, IEnumerable? preprocessorSymbols, CancellationToken cancellationToken)
- {
- var parseOption = new CSharpParseOptions(LanguageVersion.Latest, DocumentationMode.Parse, SourceCodeKind.Regular, CleanPreprocessorSymbols(preprocessorSymbols));
-
- var syntaxTrees = new List();
- var hasAnnotations = false;
- foreach (var file in IterateCsFileWithoutBinObj(directoryRoot))
- {
- var text = File.ReadAllText(NormalizeDirectorySeparators(file), Encoding.UTF8);
- var syntax = CSharpSyntaxTree.ParseText(text, parseOption);
- syntaxTrees.Add(syntax);
- if (Path.GetFileNameWithoutExtension(file) == "Attributes")
- {
- var root = await syntax.GetRootAsync(cancellationToken).ConfigureAwait(false);
- if (root.DescendantNodes().OfType().Any(x => x.Identifier.Text == "MessagePackObjectAttribute"))
- {
- hasAnnotations = true;
- }
- }
- }
-
- if (!hasAnnotations)
- {
- syntaxTrees.Add(CSharpSyntaxTree.ParseText(DummyAnnotation, parseOption));
- }
-
- var metadata = GetStandardReferences().Select(x => MetadataReference.CreateFromFile(x)).ToArray();
-
- var compilation = CSharpCompilation.Create(
- "CodeGenTemp",
- syntaxTrees,
- DistinctReference(metadata),
- new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary, allowUnsafe: true));
-
- return compilation;
- }
-
- private static IEnumerable DistinctReference(IEnumerable metadataReferences)
- {
- var set = new HashSet();
- foreach (var item in metadataReferences)
- {
- if (item.Display is object && set.Add(Path.GetFileName(item.Display)))
- {
- yield return item;
- }
- }
- }
-
- private static List GetStandardReferences()
- {
- var standardMetadataType = new[]
- {
- typeof(object),
- typeof(Attribute),
- typeof(Enumerable),
- typeof(Task<>),
- typeof(IgnoreDataMemberAttribute),
- typeof(System.Collections.Hashtable),
- typeof(System.Collections.Generic.List<>),
- typeof(System.Collections.Generic.HashSet<>),
- typeof(System.Collections.Immutable.IImmutableList<>),
- typeof(System.Linq.ILookup<,>),
- typeof(System.Tuple<>),
- typeof(System.ValueTuple<>),
- typeof(System.Collections.Concurrent.ConcurrentDictionary<,>),
- typeof(System.Collections.ObjectModel.ObservableCollection<>),
- };
-
- var metadata = standardMetadataType
- .Select(x => x.Assembly.Location)
- .Distinct()
- .ToList();
-
- var dir = new FileInfo(typeof(object).Assembly.Location).Directory ?? throw new NullReferenceException("Assembly location directory not found!");
- {
- var path = Path.Combine(dir.FullName, "netstandard.dll");
- if (File.Exists(path))
- {
- metadata.Add(path);
- }
- }
-
- {
- var path = Path.Combine(dir.FullName, "System.Runtime.dll");
- if (File.Exists(path))
- {
- metadata.Add(path);
- }
- }
-
- return metadata;
- }
-
- private static IEnumerable? CleanPreprocessorSymbols(IEnumerable? preprocessorSymbols)
- {
- return preprocessorSymbols?.Where(x => !string.IsNullOrWhiteSpace(x));
- }
-
- private static IEnumerable IterateCsFileWithoutBinObj(string root)
- {
- foreach (var item in Directory.EnumerateFiles(root, "*.cs", SearchOption.TopDirectoryOnly))
- {
- yield return item;
- }
-
- foreach (var dir in Directory.GetDirectories(root, "*", SearchOption.TopDirectoryOnly))
- {
- var dirName = new DirectoryInfo(dir).Name;
- if (dirName == "bin" || dirName == "obj")
- {
- continue;
- }
-
- foreach (var item in IterateCsFileWithoutBinObj(dir))
- {
- yield return item;
- }
- }
- }
-
- private static string NormalizeDirectorySeparators(string path)
- {
- return path.Replace('\\', Path.DirectorySeparatorChar).Replace('/', Path.DirectorySeparatorChar);
- }
-
- private const string DummyAnnotation = @"
-using System;
-
-namespace MessagePack
-{
- [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = false, Inherited = true)]
- public class MessagePackObjectAttribute : Attribute
- {
- public bool KeyAsPropertyName { get; private set; }
-
- public MessagePackObjectAttribute(bool keyAsPropertyName = false)
- {
- this.KeyAsPropertyName = keyAsPropertyName;
- }
- }
-
- [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false, Inherited = true)]
- public class KeyAttribute : Attribute
- {
- public int? IntKey { get; private set; }
- public string StringKey { get; private set; }
-
- public KeyAttribute(int x)
- {
- this.IntKey = x;
- }
-
- public KeyAttribute(string x)
- {
- this.StringKey = x;
- }
- }
-
- [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false, Inherited = true)]
- public class IgnoreMemberAttribute : Attribute
- {
- }
-
- [AttributeUsage(AttributeTargets.Interface | AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
- public class UnionAttribute : Attribute
- {
- public int Key { get; private set; }
- public Type SubType { get; private set; }
-
- public UnionAttribute(int key, Type subType)
- {
- this.Key = key;
- this.SubType = subType;
- }
- }
-
- [AttributeUsage(AttributeTargets.Constructor, AllowMultiple = false, Inherited = true)]
- public class SerializationConstructorAttribute : Attribute
- {
- }
-
- [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface | AttributeTargets.Enum | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
- public class MessagePackFormatterAttribute : Attribute
- {
- public Type FormatterType { get; private set; }
- public object[] Arguments { get; private set; }
-
- public MessagePackFormatterAttribute(Type formatterType)
- {
- this.FormatterType = formatterType;
- }
-
- public MessagePackFormatterAttribute(Type formatterType, params object[] arguments)
- {
- this.FormatterType = formatterType;
- this.Arguments = arguments;
- }
- }
-}
-
-using System;
-using System.Collections.Generic;
-using System.Text;
-
-namespace MessagePack
-{
- public interface IMessagePackSerializationCallbackReceiver
- {
- void OnBeforeSerialize();
- void OnAfterDeserialize();
- }
-}
-";
- }
-}
From e962638021fa4511878e977feca7117123de3058 Mon Sep 17 00:00:00 2001
From: y-sasaki <11359025+Y-YoL@users.noreply.github.com>
Date: Thu, 22 Dec 2022 22:55:40 +0900
Subject: [PATCH 007/660] Fix GetFormatterWithVerify missing error
---
src/MessagePack.Generator/MessagepackGenerator.Emit.cs | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/MessagePack.Generator/MessagepackGenerator.Emit.cs b/src/MessagePack.Generator/MessagepackGenerator.Emit.cs
index 921521551..e0936e576 100644
--- a/src/MessagePack.Generator/MessagepackGenerator.Emit.cs
+++ b/src/MessagePack.Generator/MessagepackGenerator.Emit.cs
@@ -109,6 +109,7 @@ private static void ResolverText(StringBuilder sb, string ns, IEnumerable
Date: Wed, 4 Jan 2023 17:09:49 +0900
Subject: [PATCH 008/660] Fix array formatters is not registered
---
.../MessagepackGenerator.Emit.cs | 33 +++++++++++--------
.../CodeAnalysis/TypeCollector.cs | 8 ++++-
2 files changed, 27 insertions(+), 14 deletions(-)
diff --git a/src/MessagePack.Generator/MessagepackGenerator.Emit.cs b/src/MessagePack.Generator/MessagepackGenerator.Emit.cs
index e0936e576..108800a02 100644
--- a/src/MessagePack.Generator/MessagepackGenerator.Emit.cs
+++ b/src/MessagePack.Generator/MessagepackGenerator.Emit.cs
@@ -30,7 +30,7 @@ private static void Generate(TypeDeclarationSyntax syntax, Compilation compilati
var (objectInfo, enumInfo, genericInfo, unionInfo) = collector.Collect();
- var code = GenerateFormatterSync(string.Empty, string.Empty, objectInfo, enumInfo, unionInfo, genericInfo);
+ var code = GenerateFormatterSync(fullType.Replace(".", "_"), string.Empty, objectInfo, enumInfo, unionInfo, genericInfo);
context.AddSource($"{fullType}.MessagePackFormatter.g.cs", code);
}
@@ -68,6 +68,18 @@ string GetNamespace(IGrouping x)
return namespaceDot + "Formatters." + x.Key;
}
+ var sb = new StringBuilder();
+ ResolverText(
+ sb,
+ namespaceDot + "Resolvers",
+ resolverName,
+ genericInfo
+ .Where(x => !x.IsOpenGenericType)
+ .Cast()
+ .Concat(enumInfo)
+ .Concat(unionInfo)
+ .Concat(objectInfo.Where(x => !x.IsOpenGenericType)));
+
var enumFormatterTemplates = enumInfo
.GroupBy(x => x.Namespace)
.Select(x => new EnumTemplate(GetNamespace(x), x.ToArray()))
@@ -78,11 +90,9 @@ string GetNamespace(IGrouping x)
.Select(x => new UnionTemplate(GetNamespace(x), x.ToArray()))
.ToArray();
- var sb = new StringBuilder();
foreach (var item in enumFormatterTemplates)
{
var text = item.TransformText();
- ResolverText(sb, item.Namespace, item.EnumSerializationInfos.Select(x => x.Name));
sb.AppendLine(text);
}
@@ -90,7 +100,6 @@ string GetNamespace(IGrouping x)
foreach (var item in unionFormatterTemplates)
{
var text = item.TransformText();
- ResolverText(sb, item.Namespace, item.UnionSerializationInfos.Select(x => x.Name));
sb.AppendLine(text);
}
@@ -98,14 +107,13 @@ string GetNamespace(IGrouping x)
foreach (var item in objectFormatterTemplates)
{
var text = item.TransformText();
- ResolverText(sb, item.Namespace, item.ObjectSerializationInfos.Select(x => x.Name));
sb.AppendLine(text);
}
return sb.ToString();
}
- private static void ResolverText(StringBuilder sb, string ns, IEnumerable names)
+ private static void ResolverText(StringBuilder sb, string ns, string resolverName, IEnumerable registerInfos)
{
var begin = $$"""
using System.Runtime.CompilerServices;
@@ -115,24 +123,23 @@ namespace {{ns}}
{
partial class FormatterRegister
{
+ [ModuleInitializer]
+ internal static void {{resolverName}}FormatterRegister()
+ {
""";
var end = $$"""
+ }
}
}
""";
sb.AppendLine(begin);
- foreach (var item in names)
+ foreach (var item in registerInfos)
{
var code = $$"""
-
- [ModuleInitializer]
- internal static void {{item}}FormatterRegister()
- {
- MessagePack.Resolvers.StaticCompositeResolver.Instance.AddGeneratedFormatter(new global::{{ns}}.{{item}}Formatter());
- }
+ MessagePack.Resolvers.StaticCompositeResolver.Instance.AddGeneratedFormatter(new global::Formatters.{{item.FormatterName}}());
""";
sb.AppendLine(code);
}
diff --git a/src/MessagePack.GeneratorCore/CodeAnalysis/TypeCollector.cs b/src/MessagePack.GeneratorCore/CodeAnalysis/TypeCollector.cs
index 9f7fd8c0f..2f2699390 100644
--- a/src/MessagePack.GeneratorCore/CodeAnalysis/TypeCollector.cs
+++ b/src/MessagePack.GeneratorCore/CodeAnalysis/TypeCollector.cs
@@ -241,6 +241,8 @@ public class TypeCollector
private readonly bool disallowInternal;
+ private readonly bool excludeArrayElement;
+
private readonly HashSet externalIgnoreTypeNames;
// visitor workspace:
@@ -292,6 +294,7 @@ public TypeCollector(Compilation compilation, bool disallowInternal, bool isForc
this.isForceUseMap = isForceUseMap;
this.externalIgnoreTypeNames = new HashSet(ignoreTypeNames ?? Array.Empty());
this.compilation = compilation;
+ this.excludeArrayElement = true;
targetTypes = new[] { targetType }
.Where(x =>
@@ -463,7 +466,10 @@ private void CollectGenericUnion(INamedTypeSymbol type)
private void CollectArray(IArrayTypeSymbol array)
{
ITypeSymbol elemType = array.ElementType;
- this.CollectCore(elemType);
+ if (!excludeArrayElement)
+ {
+ this.CollectCore(elemType);
+ }
var fullName = array.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
var elementTypeDisplayName = elemType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
From 51dac96294deeaa818ed9a409be4482e9378e01c Mon Sep 17 00:00:00 2001
From: y-sasaki <11359025+Y-YoL@users.noreply.github.com>
Date: Wed, 4 Jan 2023 17:48:45 +0900
Subject: [PATCH 009/660] Fix formatter duplicate definisions
---
.../MessagepackGenerator.Emit.cs | 8 +++---
.../MessagepackGenerator.cs | 25 +++++++++++++------
2 files changed, 23 insertions(+), 10 deletions(-)
diff --git a/src/MessagePack.Generator/MessagepackGenerator.Emit.cs b/src/MessagePack.Generator/MessagepackGenerator.Emit.cs
index 108800a02..01a2dd35a 100644
--- a/src/MessagePack.Generator/MessagepackGenerator.Emit.cs
+++ b/src/MessagePack.Generator/MessagepackGenerator.Emit.cs
@@ -97,17 +97,19 @@ string GetNamespace(IGrouping x)
}
sb.AppendLine();
- foreach (var item in unionFormatterTemplates)
+ foreach (var item in objectFormatterTemplates)
{
var text = item.TransformText();
sb.AppendLine(text);
+ return sb.ToString();
}
sb.AppendLine();
- foreach (var item in objectFormatterTemplates)
+ foreach (var item in unionFormatterTemplates)
{
var text = item.TransformText();
sb.AppendLine(text);
+ return sb.ToString();
}
return sb.ToString();
@@ -139,7 +141,7 @@ partial class FormatterRegister
foreach (var item in registerInfos)
{
var code = $$"""
- MessagePack.Resolvers.StaticCompositeResolver.Instance.AddGeneratedFormatter(new global::Formatters.{{item.FormatterName}}());
+ MessagePack.Resolvers.StaticCompositeResolver.Instance.AddGeneratedFormatter(new {{(item.FormatterName.StartsWith("global::") ? item.FormatterName : "global::Formatters." + item.FormatterName)}}());
""";
sb.AppendLine(code);
}
diff --git a/src/MessagePack.Generator/MessagepackGenerator.cs b/src/MessagePack.Generator/MessagepackGenerator.cs
index c5344dcf5..d0ce067a6 100644
--- a/src/MessagePack.Generator/MessagepackGenerator.cs
+++ b/src/MessagePack.Generator/MessagepackGenerator.cs
@@ -10,6 +10,7 @@ namespace MessagePack.Generator;
public partial class MessagepackGenerator : IIncrementalGenerator
{
public const string MessagePackObjectAttributeFullName = "MessagePack.MessagePackObjectAttribute";
+ public const string MessagePackUnionAttributeFullName = "MessagePack.UnionAttribute";
public void Initialize(IncrementalGeneratorInitializationContext context)
{
@@ -17,16 +18,26 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
MessagePackObjectAttributeFullName,
predicate: static (node, _) => node is TypeDeclarationSyntax,
transform: static (context, _) => (TypeDeclarationSyntax)context.TargetNode);
+ Register(typeDeclarations);
- var source = typeDeclarations
- .Combine(context.CompilationProvider)
- .WithComparer(Comparer.Instance);
+ var typeDeclarations2 = context.SyntaxProvider.ForAttributeWithMetadataName(
+ MessagePackUnionAttributeFullName,
+ predicate: static (node, _) => node is InterfaceDeclarationSyntax,
+ transform: static (context, _) => (TypeDeclarationSyntax)context.TargetNode);
+ Register(typeDeclarations2);
- context.RegisterSourceOutput(source, static (context, source) =>
+ void Register(IncrementalValuesProvider typeDeclarations)
{
- var (typeDeclaration, compilation) = source;
- Generate(typeDeclaration, compilation, new GeneratorContext(context));
- });
+ var source = typeDeclarations
+ .Combine(context.CompilationProvider)
+ .WithComparer(Comparer.Instance);
+
+ context.RegisterSourceOutput(source, static (context, source) =>
+ {
+ var (typeDeclaration, compilation) = source;
+ Generate(typeDeclaration, compilation, new GeneratorContext(context));
+ });
+ }
}
private class Comparer : IEqualityComparer<(TypeDeclarationSyntax, Compilation)>
From fb2395d9a7cc483f5f1a37a82b104fde93883b34 Mon Sep 17 00:00:00 2001
From: Andrew Arnott
Date: Mon, 27 Feb 2023 09:46:58 -0700
Subject: [PATCH 010/660] Add dotnet CLI tools
---
.config/dotnet-tools.json | 18 ++++++++++++++++++
init.ps1 | 5 +++++
2 files changed, 23 insertions(+)
create mode 100644 .config/dotnet-tools.json
diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json
new file mode 100644
index 000000000..2599e26ad
--- /dev/null
+++ b/.config/dotnet-tools.json
@@ -0,0 +1,18 @@
+{
+ "version": 1,
+ "isRoot": true,
+ "tools": {
+ "powershell": {
+ "version": "7.3.3",
+ "commands": [
+ "pwsh"
+ ]
+ },
+ "dotnet-format": {
+ "version": "5.1.250801",
+ "commands": [
+ "dotnet-format"
+ ]
+ }
+ }
+}
\ No newline at end of file
diff --git a/init.ps1 b/init.ps1
index ad3b4145c..5bace1e62 100755
--- a/init.ps1
+++ b/init.ps1
@@ -91,6 +91,11 @@ try {
if ($lastexitcode -ne 0) {
throw "Failure while restoring packages."
}
+
+ dotnet tool restore @RestoreArguments
+ if ($lastexitcode -ne 0) {
+ throw "Failure while restoring dotnet CLI tools."
+ }
}
& "$PSScriptRoot/tools/Set-EnvVars.ps1" -Variables $EnvVars -PrependPath $PrependPath | Out-Null
From 20876a310991753e11be38033d0a12f3e45f54ac Mon Sep 17 00:00:00 2001
From: Andrew Arnott
Date: Sun, 12 Mar 2023 17:43:59 -0600
Subject: [PATCH 011/660] Build v2.6-alpha in develop
---
version.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/version.json b/version.json
index f24af0f90..232c22237 100644
--- a/version.json
+++ b/version.json
@@ -1,6 +1,6 @@
{
"$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json",
- "version": "2.5",
+ "version": "2.6-alpha",
"publicReleaseRefSpec": [
"^refs/heads/master$",
"^refs/heads/v1\\.x$",
From ea48c8eaa182f224ca3ed1a38f2a8034d15dacce Mon Sep 17 00:00:00 2001
From: Andrew Arnott
Date: Mon, 13 Mar 2023 15:27:56 -0600
Subject: [PATCH 012/660] Updated SDK installer script to be able to find
7.0.200
---
tools/Install-DotNetSdk.ps1 | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/tools/Install-DotNetSdk.ps1 b/tools/Install-DotNetSdk.ps1
index 6bff5c278..10af50411 100644
--- a/tools/Install-DotNetSdk.ps1
+++ b/tools/Install-DotNetSdk.ps1
@@ -137,6 +137,14 @@ Function Get-InstallerExe(
if ($release.$sku.version -eq $Version) {
$filesElement = $release.$sku.files
}
+ if (!$filesElement -and ($sku -eq 'sdk') -and $release.sdks) {
+ foreach ($sdk in $release.sdks) {
+ if ($sdk.version -eq $Version) {
+ $filesElement = $sdk.files
+ break
+ }
+ }
+ }
if ($filesElement) {
foreach ($file in $filesElement) {
@@ -155,7 +163,7 @@ Function Get-InstallerExe(
if ($url) {
Get-FileFromWeb -Uri $url -OutDir $DotNetInstallScriptRoot
} else {
- Write-Error "Unable to find release of $sku v$Version"
+ throw "Unable to find release of $sku v$Version"
}
}
From 2987b4457b0242e91913e9d9f4b43b5db079fd50 Mon Sep 17 00:00:00 2001
From: Andrew Arnott
Date: Mon, 13 Mar 2023 15:31:46 -0600
Subject: [PATCH 013/660] Bump SDK version to 7.0.201
---
global.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/global.json b/global.json
index cc08211e2..cf9eefa6e 100644
--- a/global.json
+++ b/global.json
@@ -1,6 +1,6 @@
{
"sdk": {
- "version": "7.0.101",
+ "version": "7.0.201",
"rollForward": "patch",
"allowPrerelease": false
}
From f911a00e8960f75756ed0ce36b33a0afc97c8681 Mon Sep 17 00:00:00 2001
From: Andrew Arnott
Date: Mon, 20 Mar 2023 17:07:54 -0600
Subject: [PATCH 014/660] Delete MessagePack.MSBuild.Tasks project
The source generator will altogether deprecate this.
---
MessagePack.sln | 9 +-
.../MessagePack.MSBuild.Tasks.csproj | 38 ------
.../MessagePackGenerator.cs | 113 ------------------
.../build/MessagePack.MSBuild.Tasks.props | 12 --
.../build/MessagePack.MSBuild.Tasks.targets | 28 -----
5 files changed, 1 insertion(+), 199 deletions(-)
delete mode 100644 src/MessagePack.MSBuild.Tasks/MessagePack.MSBuild.Tasks.csproj
delete mode 100644 src/MessagePack.MSBuild.Tasks/MessagePackGenerator.cs
delete mode 100644 src/MessagePack.MSBuild.Tasks/build/MessagePack.MSBuild.Tasks.props
delete mode 100644 src/MessagePack.MSBuild.Tasks/build/MessagePack.MSBuild.Tasks.targets
diff --git a/MessagePack.sln b/MessagePack.sln
index d701ef3b4..bef174b68 100644
--- a/MessagePack.sln
+++ b/MessagePack.sln
@@ -1,7 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
-VisualStudioVersion = 17.5.33201.384
+VisualStudioVersion = 17.4.33103.184
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{86309CF6-0054-4CE3-BFD3-CA0AA7DB17BC}"
ProjectSection(SolutionItems) = preProject
@@ -79,8 +79,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MessagePack.GeneratorCore",
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MessagePack.Generator", "src\MessagePack.Generator\MessagePack.Generator.csproj", "{32C91908-5CAD-4C95-B240-ACBBACAC9476}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MessagePack.MSBuild.Tasks", "src\MessagePack.MSBuild.Tasks\MessagePack.MSBuild.Tasks.csproj", "{8DB135F5-A6FE-44E4-9853-7B48ED21F21B}"
-EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MessagePackAnalyzer.Tests", "tests\MessagePackAnalyzer.Tests\MessagePackAnalyzer.Tests.csproj", "{7E5FB4B9-A0F5-4B10-A1F3-03AC0BC8265A}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MessagePack.Generator.Tests", "tests\MessagePack.Generator.Tests\MessagePack.Generator.Tests.csproj", "{6AC51E68-4681-463A-B4B6-BD53517244B2}"
@@ -181,10 +179,6 @@ Global
{32C91908-5CAD-4C95-B240-ACBBACAC9476}.Debug|Any CPU.Build.0 = Debug|Any CPU
{32C91908-5CAD-4C95-B240-ACBBACAC9476}.Release|Any CPU.ActiveCfg = Release|Any CPU
{32C91908-5CAD-4C95-B240-ACBBACAC9476}.Release|Any CPU.Build.0 = Release|Any CPU
- {8DB135F5-A6FE-44E4-9853-7B48ED21F21B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {8DB135F5-A6FE-44E4-9853-7B48ED21F21B}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {8DB135F5-A6FE-44E4-9853-7B48ED21F21B}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {8DB135F5-A6FE-44E4-9853-7B48ED21F21B}.Release|Any CPU.Build.0 = Release|Any CPU
{7E5FB4B9-A0F5-4B10-A1F3-03AC0BC8265A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7E5FB4B9-A0F5-4B10-A1F3-03AC0BC8265A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7E5FB4B9-A0F5-4B10-A1F3-03AC0BC8265A}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -238,7 +232,6 @@ Global
{8D9FD130-7905-47D8-A25C-7FDEE28EA0E8} = {19FE674A-AC94-4E7E-B24C-2285D1D04CDE}
{9962132D-A271-4E68-ACC1-18FA93462552} = {86309CF6-0054-4CE3-BFD3-CA0AA7DB17BC}
{32C91908-5CAD-4C95-B240-ACBBACAC9476} = {86309CF6-0054-4CE3-BFD3-CA0AA7DB17BC}
- {8DB135F5-A6FE-44E4-9853-7B48ED21F21B} = {86309CF6-0054-4CE3-BFD3-CA0AA7DB17BC}
{7E5FB4B9-A0F5-4B10-A1F3-03AC0BC8265A} = {19FE674A-AC94-4E7E-B24C-2285D1D04CDE}
{6AC51E68-4681-463A-B4B6-BD53517244B2} = {19FE674A-AC94-4E7E-B24C-2285D1D04CDE}
{4C9BB260-62D8-49CD-9F9C-9AA6A8BFC637} = {51A614B0-E583-4DD2-AC7D-6A65634582E0}
diff --git a/src/MessagePack.MSBuild.Tasks/MessagePack.MSBuild.Tasks.csproj b/src/MessagePack.MSBuild.Tasks/MessagePack.MSBuild.Tasks.csproj
deleted file mode 100644
index 6f2e11c86..000000000
--- a/src/MessagePack.MSBuild.Tasks/MessagePack.MSBuild.Tasks.csproj
+++ /dev/null
@@ -1,38 +0,0 @@
-
-
-
- netstandard2.0
-
- true
- true
- false
- $(TargetsForTfmSpecificContentInPackage);AddBuildOutputAndDependencies
- true
- true
- MessagePack CodeGenerator Tasks
- MSBuild Tasks of MessagePack for C#.
- MsgPack;MessagePack;Serialization;Formatter;Serializer;Unity;Xamarin
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/MessagePack.MSBuild.Tasks/MessagePackGenerator.cs b/src/MessagePack.MSBuild.Tasks/MessagePackGenerator.cs
deleted file mode 100644
index 69d8f23e5..000000000
--- a/src/MessagePack.MSBuild.Tasks/MessagePackGenerator.cs
+++ /dev/null
@@ -1,113 +0,0 @@
-// Copyright (c) All contributors. All rights reserved.
-// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using MessagePackCompiler;
-using Microsoft.Build.Framework;
-using Microsoft.CodeAnalysis;
-using Microsoft.CodeAnalysis.CSharp;
-using Microsoft.CodeAnalysis.Text;
-
-// synchronous blocks aren't a problem in MSBuild tasks
-#pragma warning disable VSTHRD002 // Avoid problematic synchronous waits
-
-namespace MessagePack.MSBuild.Tasks
-{
- public class MessagePackGenerator : Microsoft.Build.Utilities.Task, ICancelableTask
- {
- private readonly CancellationTokenSource cts = new CancellationTokenSource();
-
- [Required]
- public ITaskItem[] Compile { get; set; } = null!;
-
- [Required]
- public string GeneratedOutputPath { get; set; } = null!;
-
- [Required]
- public ITaskItem[] ReferencePath { get; set; } = null!;
-
- public string? DefineConstants { get; set; }
-
- [Required]
- public string ResolverName { get; set; } = null!;
-
- public string? Namespace { get; set; }
-
- public bool UseMapMode { get; set; }
-
- public string[]? ExternalIgnoreTypeNames { get; set; }
-
- internal CancellationToken CancellationToken => this.cts.Token;
-
- public void Cancel() => this.cts.Cancel();
-
- public override bool Execute()
- {
- if (string.IsNullOrWhiteSpace(this.ResolverName))
- {
- this.Log.LogError($"{nameof(ResolverName)} task parameter must not be set to an empty value.");
- return false;
- }
-
- try
- {
- var compilation = this.CreateCompilation();
-
- var generator = new CodeGenerator(x => this.Log.LogMessage(x), CancellationToken.None);
- generator.GenerateFileAsync(
- compilation,
- this.GeneratedOutputPath,
- ResolverName,
- Namespace,
- UseMapMode,
- null,
- ExternalIgnoreTypeNames).GetAwaiter().GetResult();
- }
- catch (Exception ex)
- {
- this.Log.LogErrorFromException(ex, true);
- return false;
- }
-
- return true;
- }
-
- private Compilation CreateCompilation()
- {
- var parseOptions = new CSharpParseOptions(LanguageVersion.Latest, DocumentationMode.Parse, SourceCodeKind.Regular, this.DefineConstants?.Split(';', ','));
- var syntaxTrees = new List(this.Compile.Length);
- foreach (var path in this.Compile)
- {
- string fullPath = path.GetMetadata("FullPath");
-
- if (string.Equals(fullPath, Path.GetFullPath(this.GeneratedOutputPath), StringComparison.OrdinalIgnoreCase))
- {
- // Do not include a stale version of the file we are to generate in the compilation.
- continue;
- }
-
- using var compile = File.OpenRead(path.ItemSpec);
- var sourceText = SourceText.From(compile);
- syntaxTrees.Add(CSharpSyntaxTree.ParseText(sourceText, parseOptions, fullPath, cancellationToken: this.CancellationToken));
- }
-
- var references =
- from referencePath in this.ReferencePath
- select MetadataReference.CreateFromFile(referencePath.ItemSpec);
-
- var options = new CSharpCompilationOptions(
- OutputKind.DynamicallyLinkedLibrary);
- var compilation = CSharpCompilation.Create(
- "MsgPackTempProj",
- syntaxTrees,
- references,
- options);
- return compilation;
- }
- }
-}
diff --git a/src/MessagePack.MSBuild.Tasks/build/MessagePack.MSBuild.Tasks.props b/src/MessagePack.MSBuild.Tasks/build/MessagePack.MSBuild.Tasks.props
deleted file mode 100644
index 2139219dd..000000000
--- a/src/MessagePack.MSBuild.Tasks/build/MessagePack.MSBuild.Tasks.props
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
- MessagePack
-
-
- GeneratedResolver
-
-
- false
-
-
diff --git a/src/MessagePack.MSBuild.Tasks/build/MessagePack.MSBuild.Tasks.targets b/src/MessagePack.MSBuild.Tasks/build/MessagePack.MSBuild.Tasks.targets
deleted file mode 100644
index e95cf9e90..000000000
--- a/src/MessagePack.MSBuild.Tasks/build/MessagePack.MSBuild.Tasks.targets
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
- $(IntermediateOutputPath)mpc_generated.cs
-
-
-
-
-
-
-
-
-
-
-
-
-
From 267ba7fa71eaf7c2df8e71f8dbbbd9cae94c5b30 Mon Sep 17 00:00:00 2001
From: Andrew Arnott
Date: Mon, 20 Mar 2023 18:01:18 -0600
Subject: [PATCH 015/660] Refactor source generating projects
---
Directory.Packages.props | 8 +-
MessagePack.sln | 7 -
.../DynamicCodeDumper.csproj | 3 +
.../MessagePack.Generator.Roslyn3.csproj | 75 +-
.../CodeAnalysis/Definitions.cs | 282 +++++
.../CodeAnalysis/TypeCollector.cs | 1093 ++++++++++++++++
src/MessagePack.Generator/CodeGenerator.cs | 312 +++++
.../MessagePack.Generator.csproj | 71 +-
.../MessagepackGenerator.Emit.cs | 4 +-
.../Transforms}/EnumTemplate.cs | 6 +-
.../Transforms}/EnumTemplate.tt | 2 -
.../Transforms}/FormatterTemplate.cs | 6 +-
.../Transforms}/FormatterTemplate.tt | 2 -
.../Transforms/IFormatterTemplate.cs | 15 +
.../Transforms}/ResolverTemplate.cs | 6 +-
.../Transforms}/ResolverTemplate.tt | 2 -
.../ShouldUseFormatterResolverHelper.cs | 46 +
.../Transforms/StringKey/EmbedStringHelper.cs | 85 ++
.../StringKeyFormatterDeserializeHelper.cs | 248 ++++
.../StringKey/StringKeyFormatterTemplate.cs | 6 +-
.../StringKey/StringKeyFormatterTemplate.tt | 2 +-
.../Transforms/TemplatePartials.cs | 79 ++
.../Transforms}/UnionTemplate.cs | 6 +-
.../Transforms}/UnionTemplate.tt | 2 -
.../Utils/RoslynExtensions.cs | 48 +
src/MessagePack.GeneratorCore/.editorconfig | 4 -
.../CodeAnalysis/Definitions.cs | 282 -----
.../CodeAnalysis/TypeCollector.cs | 1094 -----------------
.../CodeGenerator.cs | 319 -----
.../Generator/IFormatterTemplate.cs | 16 -
.../ShouldUseFormatterResolverHelper.cs | 48 -
.../Generator/StringKey/EmbedStringHelper.cs | 87 --
.../StringKeyFormatterDeserializeHelper.cs | 252 ----
.../Generator/TemplatePartials.cs | 80 --
.../MessagePack.GeneratorCore.csproj | 79 --
.../Utils/RoslynExtensions.cs | 51 -
.../Internal/AutomataDictionary.cs | 91 --
.../MessagePack/Internal/AutomataKeyGen.cs | 109 ++
.../MessagePack.Generator.Tests.csproj | 2 +-
39 files changed, 2407 insertions(+), 2523 deletions(-)
create mode 100644 src/MessagePack.Generator/CodeAnalysis/Definitions.cs
create mode 100644 src/MessagePack.Generator/CodeAnalysis/TypeCollector.cs
create mode 100644 src/MessagePack.Generator/CodeGenerator.cs
rename src/{MessagePack.GeneratorCore/Generator => MessagePack.Generator/Transforms}/EnumTemplate.cs (98%)
rename src/{MessagePack.GeneratorCore/Generator => MessagePack.Generator/Transforms}/EnumTemplate.tt (94%)
rename src/{MessagePack.GeneratorCore/Generator => MessagePack.Generator/Transforms}/FormatterTemplate.cs (99%)
rename src/{MessagePack.GeneratorCore/Generator => MessagePack.Generator/Transforms}/FormatterTemplate.tt (98%)
create mode 100644 src/MessagePack.Generator/Transforms/IFormatterTemplate.cs
rename src/{MessagePack.GeneratorCore/Generator => MessagePack.Generator/Transforms}/ResolverTemplate.cs (98%)
rename src/{MessagePack.GeneratorCore/Generator => MessagePack.Generator/Transforms}/ResolverTemplate.tt (96%)
create mode 100644 src/MessagePack.Generator/Transforms/ShouldUseFormatterResolverHelper.cs
create mode 100644 src/MessagePack.Generator/Transforms/StringKey/EmbedStringHelper.cs
create mode 100644 src/MessagePack.Generator/Transforms/StringKey/StringKeyFormatterDeserializeHelper.cs
rename src/{MessagePack.GeneratorCore/Generator => MessagePack.Generator/Transforms}/StringKey/StringKeyFormatterTemplate.cs (99%)
rename src/{MessagePack.GeneratorCore/Generator => MessagePack.Generator/Transforms}/StringKey/StringKeyFormatterTemplate.tt (99%)
create mode 100644 src/MessagePack.Generator/Transforms/TemplatePartials.cs
rename src/{MessagePack.GeneratorCore/Generator => MessagePack.Generator/Transforms}/UnionTemplate.cs (98%)
rename src/{MessagePack.GeneratorCore/Generator => MessagePack.Generator/Transforms}/UnionTemplate.tt (97%)
create mode 100644 src/MessagePack.Generator/Utils/RoslynExtensions.cs
delete mode 100644 src/MessagePack.GeneratorCore/.editorconfig
delete mode 100644 src/MessagePack.GeneratorCore/CodeAnalysis/Definitions.cs
delete mode 100644 src/MessagePack.GeneratorCore/CodeAnalysis/TypeCollector.cs
delete mode 100644 src/MessagePack.GeneratorCore/CodeGenerator.cs
delete mode 100644 src/MessagePack.GeneratorCore/Generator/IFormatterTemplate.cs
delete mode 100644 src/MessagePack.GeneratorCore/Generator/ShouldUseFormatterResolverHelper.cs
delete mode 100644 src/MessagePack.GeneratorCore/Generator/StringKey/EmbedStringHelper.cs
delete mode 100644 src/MessagePack.GeneratorCore/Generator/StringKey/StringKeyFormatterDeserializeHelper.cs
delete mode 100644 src/MessagePack.GeneratorCore/Generator/TemplatePartials.cs
delete mode 100644 src/MessagePack.GeneratorCore/MessagePack.GeneratorCore.csproj
delete mode 100644 src/MessagePack.GeneratorCore/Utils/RoslynExtensions.cs
create mode 100644 src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Internal/AutomataKeyGen.cs
diff --git a/Directory.Packages.props b/Directory.Packages.props
index 5675501d0..a1f151d30 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -7,9 +7,9 @@
0.13.5
-
- 3.9.0
- 4.4.0
+
+ 4.3.0
+ 4.5.0
@@ -51,7 +51,7 @@
-
+
diff --git a/MessagePack.sln b/MessagePack.sln
index bef174b68..9c06b75f9 100644
--- a/MessagePack.sln
+++ b/MessagePack.sln
@@ -75,8 +75,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MessagePack.Internal", "san
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MessagePack.Internal.Tests", "tests\MessagePack.Internal.Tests\MessagePack.Internal.Tests.csproj", "{8D9FD130-7905-47D8-A25C-7FDEE28EA0E8}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MessagePack.GeneratorCore", "src\MessagePack.GeneratorCore\MessagePack.GeneratorCore.csproj", "{9962132D-A271-4E68-ACC1-18FA93462552}"
-EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MessagePack.Generator", "src\MessagePack.Generator\MessagePack.Generator.csproj", "{32C91908-5CAD-4C95-B240-ACBBACAC9476}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MessagePackAnalyzer.Tests", "tests\MessagePackAnalyzer.Tests\MessagePackAnalyzer.Tests.csproj", "{7E5FB4B9-A0F5-4B10-A1F3-03AC0BC8265A}"
@@ -171,10 +169,6 @@ Global
{8D9FD130-7905-47D8-A25C-7FDEE28EA0E8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8D9FD130-7905-47D8-A25C-7FDEE28EA0E8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8D9FD130-7905-47D8-A25C-7FDEE28EA0E8}.Release|Any CPU.Build.0 = Release|Any CPU
- {9962132D-A271-4E68-ACC1-18FA93462552}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {9962132D-A271-4E68-ACC1-18FA93462552}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {9962132D-A271-4E68-ACC1-18FA93462552}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {9962132D-A271-4E68-ACC1-18FA93462552}.Release|Any CPU.Build.0 = Release|Any CPU
{32C91908-5CAD-4C95-B240-ACBBACAC9476}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{32C91908-5CAD-4C95-B240-ACBBACAC9476}.Debug|Any CPU.Build.0 = Debug|Any CPU
{32C91908-5CAD-4C95-B240-ACBBACAC9476}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -230,7 +224,6 @@ Global
{4142EA80-FEF4-44A5-8553-1AE84BEBAFED} = {51A614B0-E583-4DD2-AC7D-6A65634582E0}
{C100FBA6-4164-4D6A-A532-5984D2B8DCB0} = {BF4C4202-5015-4FBD-80E6-D0F36A06F700}
{8D9FD130-7905-47D8-A25C-7FDEE28EA0E8} = {19FE674A-AC94-4E7E-B24C-2285D1D04CDE}
- {9962132D-A271-4E68-ACC1-18FA93462552} = {86309CF6-0054-4CE3-BFD3-CA0AA7DB17BC}
{32C91908-5CAD-4C95-B240-ACBBACAC9476} = {86309CF6-0054-4CE3-BFD3-CA0AA7DB17BC}
{7E5FB4B9-A0F5-4B10-A1F3-03AC0BC8265A} = {19FE674A-AC94-4E7E-B24C-2285D1D04CDE}
{6AC51E68-4681-463A-B4B6-BD53517244B2} = {19FE674A-AC94-4E7E-B24C-2285D1D04CDE}
diff --git a/sandbox/DynamicCodeDumper/DynamicCodeDumper.csproj b/sandbox/DynamicCodeDumper/DynamicCodeDumper.csproj
index ef675b4ea..9759790ae 100644
--- a/sandbox/DynamicCodeDumper/DynamicCodeDumper.csproj
+++ b/sandbox/DynamicCodeDumper/DynamicCodeDumper.csproj
@@ -31,6 +31,9 @@
Code\AutomataDictionary.cs
+
+ Code\AutomataKeyGen.cs
+
Code\ThreadsafeTypeKeyHashTable.cs
diff --git a/src/MessagePack.Generator.Roslyn3/MessagePack.Generator.Roslyn3.csproj b/src/MessagePack.Generator.Roslyn3/MessagePack.Generator.Roslyn3.csproj
index f497d4542..b25ffbde0 100644
--- a/src/MessagePack.Generator.Roslyn3/MessagePack.Generator.Roslyn3.csproj
+++ b/src/MessagePack.Generator.Roslyn3/MessagePack.Generator.Roslyn3.csproj
@@ -6,7 +6,6 @@
enable
enable
ROSLYN3
- true
cs
@@ -21,80 +20,16 @@
-
-
-
-
-
+
+
+
-
+
-
-
-
-
-
-
- True
- True
- EnumTemplate.tt
-
-
- True
- True
- FormatterTemplate.tt
-
-
- True
- True
- ResolverTemplate.tt
-
-
- True
- True
- StringKeyFormatterTemplate.tt
-
-
- %(FileName).tt
- True
- True
-
-
- True
- True
- UnionTemplate.tt
-
-
-
-
-
- TextTemplatingFilePreprocessor
- EnumTemplate.cs
- MessagePackCompiler.Generator
-
-
- TextTemplatingFilePreprocessor
- FormatterTemplate.cs
- MessagePackCompiler.Generator
-
-
- TextTemplatingFilePreprocessor
- ResolverTemplate.cs
- MessagePackCompiler.Generator
-
-
- TextTemplatingFilePreprocessor
- StringKeyFormatterTemplate.cs
- MessagePackCompiler.Generator
-
-
- TextTemplatingFilePreprocessor
- UnionTemplate.cs
- MessagePackCompiler.Generator
-
+
diff --git a/src/MessagePack.Generator/CodeAnalysis/Definitions.cs b/src/MessagePack.Generator/CodeAnalysis/Definitions.cs
new file mode 100644
index 000000000..a93a92acd
--- /dev/null
+++ b/src/MessagePack.Generator/CodeAnalysis/Definitions.cs
@@ -0,0 +1,282 @@
+// Copyright (c) All contributors. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using MessagePack.Generator.Transforms;
+
+#pragma warning disable SA1402 // File may only contain a single type
+#pragma warning disable SA1649 // File name should match first type name
+
+namespace MessagePack.Generator.CodeAnalysis;
+
+public interface INamespaceInfo
+{
+ string? Namespace { get; }
+}
+
+public interface IResolverRegisterInfo
+{
+ string FullName { get; }
+
+ string FormatterName { get; }
+}
+
+public class ObjectSerializationInfo : IResolverRegisterInfo, INamespaceInfo
+{
+ public string Name { get; }
+
+ public string FullName { get; }
+
+ public string? Namespace { get; }
+
+ public GenericTypeParameterInfo[] GenericTypeParameters { get; }
+
+ public bool IsOpenGenericType { get; }
+
+ public bool IsIntKey { get; }
+
+ public bool IsStringKey
+ {
+ get { return !this.IsIntKey; }
+ }
+
+ public bool IsClass { get; }
+
+ public MemberSerializationInfo[] ConstructorParameters { get; }
+
+ public MemberSerializationInfo[] Members { get; }
+
+ public bool HasIMessagePackSerializationCallbackReceiver { get; }
+
+ public bool NeedsCastOnBefore { get; }
+
+ public bool NeedsCastOnAfter { get; }
+
+ public string FormatterName => this.Namespace == null ? FormatterNameWithoutNameSpace : this.Namespace + "." + FormatterNameWithoutNameSpace;
+
+ public string FormatterNameWithoutNameSpace => this.Name + "Formatter" + (this.IsOpenGenericType ? $"<{string.Join(", ", this.GenericTypeParameters.Select(x => x.Name))}>" : string.Empty);
+
+ public int WriteCount
+ {
+ get
+ {
+ if (this.IsStringKey)
+ {
+ return this.Members.Count(x => x.IsReadable);
+ }
+ else
+ {
+ return this.MaxKey;
+ }
+ }
+ }
+
+ public int MaxKey
+ {
+ get
+ {
+ return this.Members.Where(x => x.IsReadable).Select(x => x.IntKey).DefaultIfEmpty(-1).Max();
+ }
+ }
+
+ public MemberSerializationInfo? GetMember(int index)
+ {
+ return this.Members.FirstOrDefault(x => x.IntKey == index);
+ }
+
+ public string GetConstructorString()
+ {
+ var args = string.Join(", ", this.ConstructorParameters.Select(x => "__" + x.Name + "__"));
+ return $"{this.FullName}({args})";
+ }
+
+ public ObjectSerializationInfo(bool isClass, bool isOpenGenericType, GenericTypeParameterInfo[] genericTypeParameterInfos, MemberSerializationInfo[] constructorParameters, bool isIntKey, MemberSerializationInfo[] members, string name, string fullName, string? @namespace, bool hasSerializationConstructor, bool needsCastOnAfter, bool needsCastOnBefore)
+ {
+ IsClass = isClass;
+ IsOpenGenericType = isOpenGenericType;
+ GenericTypeParameters = genericTypeParameterInfos;
+ ConstructorParameters = constructorParameters;
+ IsIntKey = isIntKey;
+ Members = members;
+ Name = name;
+ FullName = fullName;
+ Namespace = @namespace;
+ HasIMessagePackSerializationCallbackReceiver = hasSerializationConstructor;
+ NeedsCastOnAfter = needsCastOnAfter;
+ NeedsCastOnBefore = needsCastOnBefore;
+ }
+}
+
+public class GenericTypeParameterInfo
+{
+ public string Name { get; }
+
+ public string Constraints { get; }
+
+ public bool HasConstraints { get; }
+
+ public GenericTypeParameterInfo(string name, string constraints)
+ {
+ Name = name ?? throw new ArgumentNullException(nameof(name));
+ Constraints = constraints ?? throw new ArgumentNullException(nameof(name));
+ HasConstraints = constraints != string.Empty;
+ }
+}
+
+public class MemberSerializationInfo
+{
+ public bool IsProperty { get; }
+
+ public bool IsWritable { get; }
+
+ public bool IsReadable { get; }
+
+ public int IntKey { get; }
+
+ public string StringKey { get; }
+
+ public string Type { get; }
+
+ public string Name { get; }
+
+ public string ShortTypeName { get; }
+
+ public string? CustomFormatterTypeName { get; }
+
+ private readonly HashSet primitiveTypes = new(ShouldUseFormatterResolverHelper.PrimitiveTypes);
+
+ public MemberSerializationInfo(bool isProperty, bool isWritable, bool isReadable, int intKey, string stringKey, string name, string type, string shortTypeName, string? customFormatterTypeName)
+ {
+ IsProperty = isProperty;
+ IsWritable = isWritable;
+ IsReadable = isReadable;
+ IntKey = intKey;
+ StringKey = stringKey;
+ Type = type;
+ Name = name;
+ ShortTypeName = shortTypeName;
+ CustomFormatterTypeName = customFormatterTypeName;
+ }
+
+ public string GetSerializeMethodString()
+ {
+ if (CustomFormatterTypeName != null)
+ {
+ return $"this.__{this.Name}CustomFormatter__.Serialize(ref writer, value.{this.Name}, options)";
+ }
+ else if (this.primitiveTypes.Contains(this.Type))
+ {
+ return "writer.Write(value." + this.Name + ")";
+ }
+ else
+ {
+ return $"global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify<{this.Type}>(formatterResolver).Serialize(ref writer, value.{this.Name}, options)";
+ }
+ }
+
+ public string GetDeserializeMethodString()
+ {
+ if (CustomFormatterTypeName != null)
+ {
+ return $"this.__{this.Name}CustomFormatter__.Deserialize(ref reader, options)";
+ }
+ else if (this.primitiveTypes.Contains(this.Type))
+ {
+ if (this.Type == "byte[]")
+ {
+ return "global::MessagePack.Internal.CodeGenHelpers.GetArrayFromNullableSequence(reader.ReadBytes())";
+ }
+ else
+ {
+ return $"reader.Read{this.ShortTypeName!.Replace("[]", "s")}()";
+ }
+ }
+ else
+ {
+ return $"global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify<{this.Type}>(formatterResolver).Deserialize(ref reader, options)";
+ }
+ }
+}
+
+public class EnumSerializationInfo : IResolverRegisterInfo, INamespaceInfo
+{
+ public EnumSerializationInfo(string? @namespace, string name, string fullName, string underlyingType)
+ {
+ Namespace = @namespace;
+ Name = name;
+ FullName = fullName;
+ UnderlyingType = underlyingType;
+ }
+
+ public string? Namespace { get; }
+
+ public string Name { get; }
+
+ public string FullName { get; }
+
+ public string UnderlyingType { get; }
+
+ public string FormatterName => (this.Namespace == null ? this.Name : this.Namespace + "." + this.Name) + "Formatter";
+}
+
+public class GenericSerializationInfo : IResolverRegisterInfo, IEquatable
+{
+ public string FullName { get; }
+
+ public string FormatterName { get; }
+
+ public bool IsOpenGenericType { get; }
+
+ public bool Equals(GenericSerializationInfo? other)
+ {
+ return this.FullName.Equals(other?.FullName);
+ }
+
+ public override int GetHashCode()
+ {
+ return this.FullName.GetHashCode();
+ }
+
+ public GenericSerializationInfo(string fullName, string formatterName, bool isOpenGenericType)
+ {
+ FullName = fullName;
+ FormatterName = formatterName;
+ IsOpenGenericType = isOpenGenericType;
+ }
+}
+
+public class UnionSerializationInfo : IResolverRegisterInfo, INamespaceInfo
+{
+ public string? Namespace { get; }
+
+ public string Name { get; }
+
+ public string FullName { get; }
+
+ public string FormatterName => (this.Namespace == null ? this.Name : this.Namespace + "." + this.Name) + "Formatter";
+
+ public UnionSubTypeInfo[] SubTypes { get; }
+
+ public UnionSerializationInfo(string? @namespace, string name, string fullName, UnionSubTypeInfo[] subTypes)
+ {
+ Namespace = @namespace;
+ Name = name;
+ FullName = fullName;
+ SubTypes = subTypes;
+ }
+}
+
+public class UnionSubTypeInfo
+{
+ public UnionSubTypeInfo(int key, string type)
+ {
+ Key = key;
+ Type = type;
+ }
+
+ public int Key { get; }
+
+ public string Type { get; }
+}
diff --git a/src/MessagePack.Generator/CodeAnalysis/TypeCollector.cs b/src/MessagePack.Generator/CodeAnalysis/TypeCollector.cs
new file mode 100644
index 000000000..3e955a240
--- /dev/null
+++ b/src/MessagePack.Generator/CodeAnalysis/TypeCollector.cs
@@ -0,0 +1,1093 @@
+// Copyright (c) All contributors. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+#pragma warning disable SA1402 // File may only contain a single type
+#pragma warning disable SA1649 // File name should match first type name
+
+using System;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
+using Microsoft.CodeAnalysis;
+
+namespace MessagePack.Generator.CodeAnalysis;
+
+public class MessagePackGeneratorResolveFailedException : Exception
+{
+ public MessagePackGeneratorResolveFailedException(string message)
+ : base(message)
+ {
+ }
+}
+
+internal class ReferenceSymbols
+{
+#pragma warning disable SA1401 // Fields should be private
+ internal readonly INamedTypeSymbol? Task;
+ internal readonly INamedTypeSymbol? TaskOfT;
+ internal readonly INamedTypeSymbol MessagePackObjectAttribute;
+ internal readonly INamedTypeSymbol UnionAttribute;
+ internal readonly INamedTypeSymbol SerializationConstructorAttribute;
+ internal readonly INamedTypeSymbol KeyAttribute;
+ internal readonly INamedTypeSymbol IgnoreAttribute;
+ internal readonly INamedTypeSymbol? IgnoreDataMemberAttribute;
+ internal readonly INamedTypeSymbol IMessagePackSerializationCallbackReceiver;
+ internal readonly INamedTypeSymbol MessagePackFormatterAttribute;
+#pragma warning restore SA1401 // Fields should be private
+
+ public ReferenceSymbols(Compilation compilation, Action logger)
+ {
+ TaskOfT = compilation.GetTypeByMetadataName("System.Threading.Tasks.Task`1");
+ if (TaskOfT == null)
+ {
+ logger("failed to get metadata of System.Threading.Tasks.Task`1");
+ }
+
+ Task = compilation.GetTypeByMetadataName("System.Threading.Tasks.Task");
+ if (Task == null)
+ {
+ logger("failed to get metadata of System.Threading.Tasks.Task");
+ }
+
+ MessagePackObjectAttribute = compilation.GetTypeByMetadataName("MessagePack.MessagePackObjectAttribute")
+ ?? throw new InvalidOperationException("failed to get metadata of MessagePack.MessagePackObjectAttribute");
+
+ UnionAttribute = compilation.GetTypeByMetadataName("MessagePack.UnionAttribute")
+ ?? throw new InvalidOperationException("failed to get metadata of MessagePack.UnionAttribute");
+
+ SerializationConstructorAttribute = compilation.GetTypeByMetadataName("MessagePack.SerializationConstructorAttribute")
+ ?? throw new InvalidOperationException("failed to get metadata of MessagePack.SerializationConstructorAttribute");
+
+ KeyAttribute = compilation.GetTypeByMetadataName("MessagePack.KeyAttribute")
+ ?? throw new InvalidOperationException("failed to get metadata of MessagePack.KeyAttribute");
+
+ IgnoreAttribute = compilation.GetTypeByMetadataName("MessagePack.IgnoreMemberAttribute")
+ ?? throw new InvalidOperationException("failed to get metadata of MessagePack.IgnoreMemberAttribute");
+
+ IgnoreDataMemberAttribute = compilation.GetTypeByMetadataName("System.Runtime.Serialization.IgnoreDataMemberAttribute");
+ if (IgnoreDataMemberAttribute == null)
+ {
+ logger("failed to get metadata of System.Runtime.Serialization.IgnoreDataMemberAttribute");
+ }
+
+ IMessagePackSerializationCallbackReceiver = compilation.GetTypeByMetadataName("MessagePack.IMessagePackSerializationCallbackReceiver")
+ ?? throw new InvalidOperationException("failed to get metadata of MessagePack.IMessagePackSerializationCallbackReceiver");
+
+ MessagePackFormatterAttribute = compilation.GetTypeByMetadataName("MessagePack.MessagePackFormatterAttribute")
+ ?? throw new InvalidOperationException("failed to get metadata of MessagePack.MessagePackFormatterAttribute");
+ }
+}
+
+public class TypeCollector
+{
+ private static readonly SymbolDisplayFormat BinaryWriteFormat = new SymbolDisplayFormat(
+ genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters,
+ miscellaneousOptions: SymbolDisplayMiscellaneousOptions.ExpandNullable,
+ typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameOnly);
+
+ private static readonly SymbolDisplayFormat ShortTypeNameFormat = new SymbolDisplayFormat(
+ typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypes);
+
+ private readonly bool isForceUseMap;
+ private readonly ReferenceSymbols typeReferences;
+ private readonly ITypeSymbol[] targetTypes;
+ private readonly HashSet embeddedTypes = new(new[]
+ {
+ "short",
+ "int",
+ "long",
+ "ushort",
+ "uint",
+ "ulong",
+ "float",
+ "double",
+ "bool",
+ "byte",
+ "sbyte",
+ "decimal",
+ "char",
+ "string",
+ "object",
+ "System.Guid",
+ "System.TimeSpan",
+ "System.DateTime",
+ "System.DateTimeOffset",
+
+ "MessagePack.Nil",
+
+ // and arrays
+ "short[]",
+ "int[]",
+ "long[]",
+ "ushort[]",
+ "uint[]",
+ "ulong[]",
+ "float[]",
+ "double[]",
+ "bool[]",
+ "byte[]",
+ "sbyte[]",
+ "decimal[]",
+ "char[]",
+ "string[]",
+ "System.DateTime[]",
+ "System.ArraySegment",
+ "System.ArraySegment?",
+
+ // extensions
+ "UnityEngine.Vector2",
+ "UnityEngine.Vector3",
+ "UnityEngine.Vector4",
+ "UnityEngine.Quaternion",
+ "UnityEngine.Color",
+ "UnityEngine.Bounds",
+ "UnityEngine.Rect",
+ "UnityEngine.AnimationCurve",
+ "UnityEngine.RectOffset",
+ "UnityEngine.Gradient",
+ "UnityEngine.WrapMode",
+ "UnityEngine.GradientMode",
+ "UnityEngine.Keyframe",
+ "UnityEngine.Matrix4x4",
+ "UnityEngine.GradientColorKey",
+ "UnityEngine.GradientAlphaKey",
+ "UnityEngine.Color32",
+ "UnityEngine.LayerMask",
+ "UnityEngine.Vector2Int",
+ "UnityEngine.Vector3Int",
+ "UnityEngine.RangeInt",
+ "UnityEngine.RectInt",
+ "UnityEngine.BoundsInt",
+
+ "System.Reactive.Unit",
+ });
+
+ private readonly Dictionary knownGenericTypes = new()
+ {
+#pragma warning disable SA1509 // Opening braces should not be preceded by blank line
+ { "System.Collections.Generic.List<>", "global::MessagePack.Formatters.ListFormatter" },
+ { "System.Collections.Generic.LinkedList<>", "global::MessagePack.Formatters.LinkedListFormatter" },
+ { "System.Collections.Generic.Queue<>", "global::MessagePack.Formatters.QueueFormatter" },
+ { "System.Collections.Generic.Stack<>", "global::MessagePack.Formatters.StackFormatter" },
+ { "System.Collections.Generic.HashSet<>", "global::MessagePack.Formatters.HashSetFormatter" },
+ { "System.Collections.ObjectModel.ReadOnlyCollection<>", "global::MessagePack.Formatters.ReadOnlyCollectionFormatter" },
+ { "System.Collections.Generic.IList<>", "global::MessagePack.Formatters.InterfaceListFormatter2" },
+ { "System.Collections.Generic.ICollection<>", "global::MessagePack.Formatters.InterfaceCollectionFormatter2" },
+ { "System.Collections.Generic.IEnumerable<>", "global::MessagePack.Formatters.InterfaceEnumerableFormatter" },
+ { "System.Collections.Generic.Dictionary<,>", "global::MessagePack.Formatters.DictionaryFormatter" },
+ { "System.Collections.Generic.IDictionary<,>", "global::MessagePack.Formatters.InterfaceDictionaryFormatter" },
+ { "System.Collections.Generic.SortedDictionary<,>", "global::MessagePack.Formatters.SortedDictionaryFormatter" },
+ { "System.Collections.Generic.SortedList<,>", "global::MessagePack.Formatters.SortedListFormatter" },
+ { "System.Linq.ILookup<,>", "global::MessagePack.Formatters.InterfaceLookupFormatter" },
+ { "System.Linq.IGrouping<,>", "global::MessagePack.Formatters.InterfaceGroupingFormatter" },
+ { "System.Collections.ObjectModel.ObservableCollection<>", "global::MessagePack.Formatters.ObservableCollectionFormatter" },
+ { "System.Collections.ObjectModel.ReadOnlyObservableCollection<>", "global::MessagePack.Formatters.ReadOnlyObservableCollectionFormatter" },
+ { "System.Collections.Generic.IReadOnlyList<>", "global::MessagePack.Formatters.InterfaceReadOnlyListFormatter" },
+ { "System.Collections.Generic.IReadOnlyCollection<>", "global::MessagePack.Formatters.InterfaceReadOnlyCollectionFormatter" },
+ { "System.Collections.Generic.ISet<>", "global::MessagePack.Formatters.InterfaceSetFormatter" },
+ { "System.Collections.Concurrent.ConcurrentBag<>", "global::MessagePack.Formatters.ConcurrentBagFormatter" },
+ { "System.Collections.Concurrent.ConcurrentQueue<>", "global::MessagePack.Formatters.ConcurrentQueueFormatter" },
+ { "System.Collections.Concurrent.ConcurrentStack<>", "global::MessagePack.Formatters.ConcurrentStackFormatter" },
+ { "System.Collections.ObjectModel.ReadOnlyDictionary<,>", "global::MessagePack.Formatters.ReadOnlyDictionaryFormatter" },
+ { "System.Collections.Generic.IReadOnlyDictionary<,>", "global::MessagePack.Formatters.InterfaceReadOnlyDictionaryFormatter" },
+ { "System.Collections.Concurrent.ConcurrentDictionary<,>", "global::MessagePack.Formatters.ConcurrentDictionaryFormatter" },
+ { "System.Lazy<>", "global::MessagePack.Formatters.LazyFormatter" },
+ { "System.Threading.Tasks<>", "global::MessagePack.Formatters.TaskValueFormatter" },
+
+ { "System.Tuple<>", "global::MessagePack.Formatters.TupleFormatter" },
+ { "System.Tuple<,>", "global::MessagePack.Formatters.TupleFormatter" },
+ { "System.Tuple<,,>", "global::MessagePack.Formatters.TupleFormatter" },
+ { "System.Tuple<,,,>", "global::MessagePack.Formatters.TupleFormatter" },
+ { "System.Tuple<,,,,>", "global::MessagePack.Formatters.TupleFormatter" },
+ { "System.Tuple<,,,,,>", "global::MessagePack.Formatters.TupleFormatter" },
+ { "System.Tuple<,,,,,,>", "global::MessagePack.Formatters.TupleFormatter" },
+ { "System.Tuple<,,,,,,,>", "global::MessagePack.Formatters.TupleFormatter" },
+
+ { "System.ValueTuple<>", "global::MessagePack.Formatters.ValueTupleFormatter" },
+ { "System.ValueTuple<,>", "global::MessagePack.Formatters.ValueTupleFormatter" },
+ { "System.ValueTuple<,,>", "global::MessagePack.Formatters.ValueTupleFormatter" },
+ { "System.ValueTuple<,,,>", "global::MessagePack.Formatters.ValueTupleFormatter" },
+ { "System.ValueTuple<,,,,>", "global::MessagePack.Formatters.ValueTupleFormatter" },
+ { "System.ValueTuple<,,,,,>", "global::MessagePack.Formatters.ValueTupleFormatter" },
+ { "System.ValueTuple<,,,,,,>", "global::MessagePack.Formatters.ValueTupleFormatter" },
+ { "System.ValueTuple<,,,,,,,>", "global::MessagePack.Formatters.ValueTupleFormatter" },
+
+ { "System.Collections.Generic.KeyValuePair<,>", "global::MessagePack.Formatters.KeyValuePairFormatter" },
+ { "System.Threading.Tasks.ValueTask<>", "global::MessagePack.Formatters.KeyValuePairFormatter" },
+ { "System.ArraySegment<>", "global::MessagePack.Formatters.ArraySegmentFormatter" },
+
+ // extensions
+ { "System.Collections.Immutable.ImmutableArray<>", "global::MessagePack.ImmutableCollection.ImmutableArrayFormatter" },
+ { "System.Collections.Immutable.ImmutableList<>", "global::MessagePack.ImmutableCollection.ImmutableListFormatter" },
+ { "System.Collections.Immutable.ImmutableDictionary<,>", "global::MessagePack.ImmutableCollection.ImmutableDictionaryFormatter" },
+ { "System.Collections.Immutable.ImmutableHashSet<>", "global::MessagePack.ImmutableCollection.ImmutableHashSetFormatter" },
+ { "System.Collections.Immutable.ImmutableSortedDictionary<,>", "global::MessagePack.ImmutableCollection.ImmutableSortedDictionaryFormatter" },
+ { "System.Collections.Immutable.ImmutableSortedSet<>", "global::MessagePack.ImmutableCollection.ImmutableSortedSetFormatter" },
+ { "System.Collections.Immutable.ImmutableQueue<>", "global::MessagePack.ImmutableCollection.ImmutableQueueFormatter" },
+ { "System.Collections.Immutable.ImmutableStack<>", "global::MessagePack.ImmutableCollection.ImmutableStackFormatter" },
+ { "System.Collections.Immutable.IImmutableList<>", "global::MessagePack.ImmutableCollection.InterfaceImmutableListFormatter" },
+ { "System.Collections.Immutable.IImmutableDictionary<,>", "global::MessagePack.ImmutableCollection.InterfaceImmutableDictionaryFormatter" },
+ { "System.Collections.Immutable.IImmutableQueue<>", "global::MessagePack.ImmutableCollection.InterfaceImmutableQueueFormatter" },
+ { "System.Collections.Immutable.IImmutableSet<>", "global::MessagePack.ImmutableCollection.InterfaceImmutableSetFormatter" },
+ { "System.Collections.Immutable.IImmutableStack<>", "global::MessagePack.ImmutableCollection.InterfaceImmutableStackFormatter" },
+
+ { "Reactive.Bindings.ReactiveProperty<>", "global::MessagePack.ReactivePropertyExtension.ReactivePropertyFormatter" },
+ { "Reactive.Bindings.IReactiveProperty<>", "global::MessagePack.ReactivePropertyExtension.InterfaceReactivePropertyFormatter" },
+ { "Reactive.Bindings.IReadOnlyReactiveProperty<>", "global::MessagePack.ReactivePropertyExtension.InterfaceReadOnlyReactivePropertyFormatter" },
+ { "Reactive.Bindings.ReactiveCollection<>", "global::MessagePack.ReactivePropertyExtension.ReactiveCollectionFormatter" },
+#pragma warning restore SA1509 // Opening braces should not be preceded by blank line
+ };
+
+ private readonly bool disallowInternal;
+
+ private readonly bool excludeArrayElement;
+
+ private readonly HashSet externalIgnoreTypeNames;
+
+ // visitor workspace:
+#pragma warning disable RS1024 // Compare symbols correctly (https://github.com/dotnet/roslyn-analyzers/issues/5246)
+ private readonly HashSet alreadyCollected = new(SymbolEqualityComparer.Default);
+#pragma warning restore RS1024 // Compare symbols correctly
+ private readonly List collectedObjectInfo = new();
+ private readonly List collectedEnumInfo = new();
+ private readonly List collectedGenericInfo = new();
+ private readonly List collectedUnionInfo = new();
+
+ private readonly Compilation compilation;
+
+ public TypeCollector(Compilation compilation, bool disallowInternal, bool isForceUseMap, string[]? ignoreTypeNames, Action logger)
+ {
+ this.typeReferences = new ReferenceSymbols(compilation, logger);
+ this.disallowInternal = disallowInternal;
+ this.isForceUseMap = isForceUseMap;
+ this.externalIgnoreTypeNames = new HashSet(ignoreTypeNames ?? Array.Empty());
+ this.compilation = compilation;
+
+ targetTypes = compilation.GetNamedTypeSymbols()
+ .Where(x =>
+ {
+ if (x.DeclaredAccessibility == Accessibility.Public)
+ {
+ return true;
+ }
+
+ if (!disallowInternal)
+ {
+ return x.DeclaredAccessibility == Accessibility.Friend;
+ }
+
+ return false;
+ })
+ .Where(x =>
+ ((x.TypeKind == TypeKind.Interface) && x.GetAttributes().Any(x2 => x2.AttributeClass.ApproximatelyEqual(typeReferences.UnionAttribute)))
+ || ((x.TypeKind == TypeKind.Class && x.IsAbstract) && x.GetAttributes().Any(x2 => x2.AttributeClass.ApproximatelyEqual(typeReferences.UnionAttribute)))
+ || ((x.TypeKind == TypeKind.Class) && x.GetAttributes().Any(x2 => x2.AttributeClass.ApproximatelyEqual(typeReferences.MessagePackObjectAttribute)))
+ || ((x.TypeKind == TypeKind.Struct) && x.GetAttributes().Any(x2 => x2.AttributeClass.ApproximatelyEqual(typeReferences.MessagePackObjectAttribute))))
+ .ToArray();
+ }
+
+ public TypeCollector(Compilation compilation, bool disallowInternal, bool isForceUseMap, string[]? ignoreTypeNames, ITypeSymbol targetType)
+ {
+ this.typeReferences = new ReferenceSymbols(compilation, _ => { });
+ this.disallowInternal = disallowInternal;
+ this.isForceUseMap = isForceUseMap;
+ this.externalIgnoreTypeNames = new HashSet(ignoreTypeNames ?? Array.Empty());
+ this.compilation = compilation;
+ this.excludeArrayElement = true;
+
+ targetTypes = new[] { targetType }
+ .Where(x =>
+ {
+ if (x.DeclaredAccessibility == Accessibility.Public)
+ {
+ return true;
+ }
+
+ if (!disallowInternal)
+ {
+ return x.DeclaredAccessibility == Accessibility.Friend;
+ }
+
+ return false;
+ })
+ .Where(x =>
+ ((x.TypeKind == TypeKind.Interface) && x.GetAttributes().Any(x2 => x2.AttributeClass.ApproximatelyEqual(typeReferences.UnionAttribute)))
+ || ((x.TypeKind == TypeKind.Class && x.IsAbstract) && x.GetAttributes().Any(x2 => x2.AttributeClass.ApproximatelyEqual(typeReferences.UnionAttribute)))
+ || ((x.TypeKind == TypeKind.Class) && x.GetAttributes().Any(x2 => x2.AttributeClass.ApproximatelyEqual(typeReferences.MessagePackObjectAttribute)))
+ || ((x.TypeKind == TypeKind.Struct) && x.GetAttributes().Any(x2 => x2.AttributeClass.ApproximatelyEqual(typeReferences.MessagePackObjectAttribute))))
+ .ToArray();
+ }
+
+ private void ResetWorkspace()
+ {
+ this.alreadyCollected.Clear();
+ this.collectedObjectInfo.Clear();
+ this.collectedEnumInfo.Clear();
+ this.collectedGenericInfo.Clear();
+ this.collectedUnionInfo.Clear();
+ }
+
+ // EntryPoint
+ public (ObjectSerializationInfo[] ObjectInfo, EnumSerializationInfo[] EnumInfo, GenericSerializationInfo[] GenericInfo, UnionSerializationInfo[] UnionInfo) Collect()
+ {
+ this.ResetWorkspace();
+
+ foreach (var item in this.targetTypes)
+ {
+ this.CollectCore(item);
+ }
+
+ return (
+ this.collectedObjectInfo.OrderBy(x => x.FullName).ToArray(),
+ this.collectedEnumInfo.OrderBy(x => x.FullName).ToArray(),
+ this.collectedGenericInfo.Distinct().OrderBy(x => x.FullName).ToArray(),
+ this.collectedUnionInfo.OrderBy(x => x.FullName).ToArray());
+ }
+
+ // Gate of recursive collect
+ private void CollectCore(ITypeSymbol typeSymbol)
+ {
+ if (!this.alreadyCollected.Add(typeSymbol))
+ {
+ return;
+ }
+
+ var typeSymbolString = typeSymbol.WithNullableAnnotation(NullableAnnotation.NotAnnotated).ToString() ?? throw new InvalidOperationException();
+ if (this.embeddedTypes.Contains(typeSymbolString))
+ {
+ return;
+ }
+
+ if (this.externalIgnoreTypeNames.Contains(typeSymbolString))
+ {
+ return;
+ }
+
+ if (typeSymbol is IArrayTypeSymbol arrayTypeSymbol)
+ {
+ this.CollectArray((IArrayTypeSymbol)ToTupleUnderlyingType(arrayTypeSymbol));
+ return;
+ }
+
+ if (!this.IsAllowAccessibility(typeSymbol))
+ {
+ return;
+ }
+
+ if (!(typeSymbol is INamedTypeSymbol type))
+ {
+ return;
+ }
+
+ var customFormatterAttr = typeSymbol.GetAttributes().FirstOrDefault(x => x.AttributeClass.ApproximatelyEqual(this.typeReferences.MessagePackFormatterAttribute));
+ if (customFormatterAttr != null)
+ {
+ return;
+ }
+
+ if (type.EnumUnderlyingType != null)
+ {
+ this.CollectEnum(type, type.EnumUnderlyingType);
+ return;
+ }
+
+ if (type.IsGenericType)
+ {
+ this.CollectGeneric((INamedTypeSymbol)ToTupleUnderlyingType(type));
+ return;
+ }
+
+ if (type.Locations[0].IsInMetadata)
+ {
+ return;
+ }
+
+ if (type.TypeKind == TypeKind.Interface || (type.TypeKind == TypeKind.Class && type.IsAbstract))
+ {
+ this.CollectUnion(type);
+ return;
+ }
+
+ this.CollectObject(type);
+ }
+
+ private void CollectEnum(INamedTypeSymbol type, ISymbol enumUnderlyingType)
+ {
+ var info = new EnumSerializationInfo(type.ContainingNamespace.IsGlobalNamespace ? null : type.ContainingNamespace.ToDisplayString(), type.ToDisplayString(ShortTypeNameFormat).Replace(".", "_"), type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), enumUnderlyingType.ToDisplayString(BinaryWriteFormat));
+ this.collectedEnumInfo.Add(info);
+ }
+
+ private void CollectUnion(INamedTypeSymbol type)
+ {
+ ImmutableArray[] unionAttrs = type.GetAttributes().Where(x => x.AttributeClass.ApproximatelyEqual(this.typeReferences.UnionAttribute)).Select(x => x.ConstructorArguments).ToArray();
+ if (unionAttrs.Length == 0)
+ {
+ throw new MessagePackGeneratorResolveFailedException("Serialization Type must mark UnionAttribute." + " type: " + type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat));
+ }
+
+ // 0, Int 1, SubType
+ UnionSubTypeInfo UnionSubTypeInfoSelector(ImmutableArray x)
+ {
+ if (!(x[0] is { Value: int key }) || !(x[1] is { Value: ITypeSymbol typeSymbol }))
+ {
+ throw new NotSupportedException("AOT code generation only supports UnionAttribute that uses a Type parameter, but the " + type.ToDisplayString(SymbolDisplayFormat.CSharpErrorMessageFormat) + " type uses an unsupported parameter.");
+ }
+
+ var typeName = typeSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
+ return new UnionSubTypeInfo(key, typeName);
+ }
+
+ var info = new UnionSerializationInfo(type.ContainingNamespace.IsGlobalNamespace ? null : type.ContainingNamespace.ToDisplayString(), type.Name, type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), unionAttrs.Select(UnionSubTypeInfoSelector).OrderBy(x => x.Key).ToArray());
+
+ this.collectedUnionInfo.Add(info);
+ }
+
+ private void CollectGenericUnion(INamedTypeSymbol type)
+ {
+ var unionAttrs = type.GetAttributes().Where(x => x.AttributeClass.ApproximatelyEqual(this.typeReferences.UnionAttribute)).Select(x => x.ConstructorArguments);
+ using var enumerator = unionAttrs.GetEnumerator();
+ if (!enumerator.MoveNext())
+ {
+ return;
+ }
+
+ do
+ {
+ var x = enumerator.Current;
+ if (x[1] is { Value: INamedTypeSymbol unionType } && alreadyCollected.Contains(unionType) == false)
+ {
+ CollectCore(unionType);
+ }
+ }
+ while (enumerator.MoveNext());
+ }
+
+ private void CollectArray(IArrayTypeSymbol array)
+ {
+ ITypeSymbol elemType = array.ElementType;
+ if (!excludeArrayElement)
+ {
+ this.CollectCore(elemType);
+ }
+
+ var fullName = array.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
+ var elementTypeDisplayName = elemType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
+ string formatterName;
+ if (array.IsSZArray)
+ {
+ formatterName = "global::MessagePack.Formatters.ArrayFormatter<" + elementTypeDisplayName + ">";
+ }
+ else
+ {
+ formatterName = array.Rank switch
+ {
+ 2 => "global::MessagePack.Formatters.TwoDimensionalArrayFormatter<" + elementTypeDisplayName + ">",
+ 3 => "global::MessagePack.Formatters.ThreeDimensionalArrayFormatter<" + elementTypeDisplayName + ">",
+ 4 => "global::MessagePack.Formatters.FourDimensionalArrayFormatter<" + elementTypeDisplayName + ">",
+ _ => throw new InvalidOperationException("does not supports array dimension, " + fullName),
+ };
+ }
+
+ var info = new GenericSerializationInfo(fullName, formatterName, elemType is ITypeParameterSymbol);
+ this.collectedGenericInfo.Add(info);
+ }
+
+ private ITypeSymbol ToTupleUnderlyingType(ITypeSymbol typeSymbol)
+ {
+ if (typeSymbol is IArrayTypeSymbol array)
+ {
+ return compilation.CreateArrayTypeSymbol(ToTupleUnderlyingType(array.ElementType), array.Rank);
+ }
+
+ if (typeSymbol is not INamedTypeSymbol namedType || !namedType.IsGenericType)
+ {
+ return typeSymbol;
+ }
+
+ namedType = namedType.TupleUnderlyingType ?? namedType;
+ var newTypeArguments = namedType.TypeArguments.Select(ToTupleUnderlyingType).ToArray();
+ if (!namedType.TypeArguments.SequenceEqual(newTypeArguments))
+ {
+ return namedType.ConstructedFrom.Construct(newTypeArguments);
+ }
+
+ return namedType;
+ }
+
+ private void CollectGeneric(INamedTypeSymbol type)
+ {
+ INamedTypeSymbol genericType = type.ConstructUnboundGenericType();
+ var genericTypeString = genericType.ToDisplayString();
+ var fullName = type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
+ var isOpenGenericType = IsOpenGenericTypeRecursively(type);
+
+ // special case
+ if (fullName == "global::System.ArraySegment" || fullName == "global::System.ArraySegment?")
+ {
+ return;
+ }
+
+ // nullable
+ if (genericTypeString == "T?")
+ {
+ var firstTypeArgument = type.TypeArguments[0];
+ this.CollectCore(firstTypeArgument);
+
+ if (this.embeddedTypes.Contains(firstTypeArgument.ToString()!))
+ {
+ return;
+ }
+
+ var info = new GenericSerializationInfo(type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), "global::MessagePack.Formatters.NullableFormatter<" + firstTypeArgument.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) + ">", isOpenGenericType);
+ this.collectedGenericInfo.Add(info);
+ return;
+ }
+
+ // collection
+ if (this.knownGenericTypes.TryGetValue(genericTypeString, out var formatter))
+ {
+ foreach (ITypeSymbol item in type.TypeArguments)
+ {
+ this.CollectCore(item);
+ }
+
+ var typeArgs = string.Join(", ", type.TypeArguments.Select(x => x.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)));
+ var f = formatter.Replace("TREPLACE", typeArgs);
+
+ var info = new GenericSerializationInfo(type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), f, isOpenGenericType);
+
+ this.collectedGenericInfo.Add(info);
+
+ if (genericTypeString != "System.Linq.ILookup<,>")
+ {
+ return;
+ }
+
+ formatter = this.knownGenericTypes["System.Linq.IGrouping<,>"];
+ f = formatter.Replace("TREPLACE", typeArgs);
+
+ var groupingInfo = new GenericSerializationInfo("global::System.Linq.IGrouping<" + typeArgs + ">", f, isOpenGenericType);
+ this.collectedGenericInfo.Add(groupingInfo);
+
+ formatter = this.knownGenericTypes["System.Collections.Generic.IEnumerable<>"];
+ typeArgs = type.TypeArguments[1].ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
+ f = formatter.Replace("TREPLACE", typeArgs);
+
+ var enumerableInfo = new GenericSerializationInfo("global::System.Collections.Generic.IEnumerable<" + typeArgs + ">", f, isOpenGenericType);
+ this.collectedGenericInfo.Add(enumerableInfo);
+ return;
+ }
+
+ // Generic types
+ if (type.IsDefinition)
+ {
+ this.CollectGenericUnion(type);
+ this.CollectObject(type);
+ return;
+ }
+ else
+ {
+ // Collect substituted types for the properties and fields.
+ // NOTE: It is used to register formatters from nested generic type.
+ // However, closed generic types such as `Foo` are not registered as a formatter.
+ GetObjectInfo(type);
+
+ // Collect generic type definition, that is not collected when it is defined outside target project.
+ CollectCore(type.OriginalDefinition);
+ }
+
+ // Collect substituted types for the type parameters (e.g. Bar in Foo)
+ foreach (var item in type.TypeArguments)
+ {
+ this.CollectCore(item);
+ }
+
+ var formatterBuilder = new StringBuilder();
+ if (!type.ContainingNamespace.IsGlobalNamespace)
+ {
+ formatterBuilder.Append(type.ContainingNamespace.ToDisplayString() + ".");
+ }
+
+ formatterBuilder.Append(type.Name);
+ formatterBuilder.Append("Formatter<");
+ var typeArgumentIterator = type.TypeArguments.GetEnumerator();
+ {
+ if (typeArgumentIterator.MoveNext())
+ {
+ formatterBuilder.Append(typeArgumentIterator.Current.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat));
+ }
+
+ while (typeArgumentIterator.MoveNext())
+ {
+ formatterBuilder.Append(", ");
+ formatterBuilder.Append(typeArgumentIterator.Current.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat));
+ }
+ }
+
+ formatterBuilder.Append('>');
+
+ var genericSerializationInfo = new GenericSerializationInfo(type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), formatterBuilder.ToString(), isOpenGenericType);
+ this.collectedGenericInfo.Add(genericSerializationInfo);
+ }
+
+ private void CollectObject(INamedTypeSymbol type)
+ {
+ ObjectSerializationInfo info = GetObjectInfo(type);
+ collectedObjectInfo.Add(info);
+ }
+
+ private ObjectSerializationInfo GetObjectInfo(INamedTypeSymbol type)
+ {
+ var isClass = !type.IsValueType;
+ var isOpenGenericType = type.IsGenericType;
+
+ AttributeData contractAttr = type.GetAttributes().FirstOrDefault(x => x.AttributeClass.ApproximatelyEqual(this.typeReferences.MessagePackObjectAttribute))
+ ?? throw new MessagePackGeneratorResolveFailedException("Serialization Object must mark MessagePackObjectAttribute." + " type: " + type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat));
+
+ var isIntKey = true;
+ var intMembers = new Dictionary();
+ var stringMembers = new Dictionary();
+
+ if (this.isForceUseMap || (contractAttr.ConstructorArguments[0] is { Value: bool firstConstructorArgument } && firstConstructorArgument))
+ {
+ // All public members are serialize target except [Ignore] member.
+ isIntKey = false;
+
+ var hiddenIntKey = 0;
+
+ foreach (IPropertySymbol item in type.GetAllMembers().OfType().Where(x => !x.IsOverride))
+ {
+ if (item.GetAttributes().Any(x => (x.AttributeClass.ApproximatelyEqual(this.typeReferences.IgnoreAttribute) || x.AttributeClass?.Name == this.typeReferences.IgnoreDataMemberAttribute?.Name)))
+ {
+ continue;
+ }
+
+ var isReadable = item.GetMethod != null && item.GetMethod.DeclaredAccessibility == Accessibility.Public && !item.IsStatic;
+ var isWritable = item.SetMethod != null && item.SetMethod.DeclaredAccessibility == Accessibility.Public && !item.IsStatic;
+ if (!isReadable && !isWritable)
+ {
+ continue;
+ }
+
+ var customFormatterAttr = item.GetAttributes().FirstOrDefault(x => x.AttributeClass.ApproximatelyEqual(this.typeReferences.MessagePackFormatterAttribute))?.ConstructorArguments[0].Value as INamedTypeSymbol;
+ var member = new MemberSerializationInfo(true, isWritable, isReadable, hiddenIntKey++, item.Name, item.Name, item.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), item.Type.ToDisplayString(BinaryWriteFormat), customFormatterAttr?.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat));
+ stringMembers.Add(member.StringKey, member);
+
+ this.CollectCore(item.Type); // recursive collect
+ }
+
+ foreach (IFieldSymbol item in type.GetAllMembers().OfType())
+ {
+ if (item.GetAttributes().Any(x => (x.AttributeClass.ApproximatelyEqual(this.typeReferences.IgnoreAttribute) || x.AttributeClass?.Name == this.typeReferences.IgnoreDataMemberAttribute?.Name)))
+ {
+ continue;
+ }
+
+ if (item.IsImplicitlyDeclared)
+ {
+ continue;
+ }
+
+ var isReadable = item.DeclaredAccessibility == Accessibility.Public && !item.IsStatic;
+ var isWritable = item.DeclaredAccessibility == Accessibility.Public && !item.IsReadOnly && !item.IsStatic;
+ if (!isReadable && !isWritable)
+ {
+ continue;
+ }
+
+ var customFormatterAttr = item.GetAttributes().FirstOrDefault(x => x.AttributeClass.ApproximatelyEqual(this.typeReferences.MessagePackFormatterAttribute))?.ConstructorArguments[0].Value as INamedTypeSymbol;
+ var member = new MemberSerializationInfo(false, isWritable, isReadable, hiddenIntKey++, item.Name, item.Name, item.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), item.Type.ToDisplayString(BinaryWriteFormat), customFormatterAttr?.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat));
+ stringMembers.Add(member.StringKey, member);
+ this.CollectCore(item.Type); // recursive collect
+ }
+ }
+ else
+ {
+ // Only KeyAttribute members
+ var searchFirst = true;
+ var hiddenIntKey = 0;
+
+ foreach (IPropertySymbol item in type.GetAllMembers().OfType())
+ {
+ if (item.IsIndexer)
+ {
+ continue; // .tt files don't generate good code for this yet: https://github.com/neuecc/MessagePack-CSharp/issues/390
+ }
+
+ if (item.GetAttributes().Any(x =>
+ {
+ var typeReferencesIgnoreDataMemberAttribute = this.typeReferences.IgnoreDataMemberAttribute;
+ return typeReferencesIgnoreDataMemberAttribute != null && (x.AttributeClass.ApproximatelyEqual(this.typeReferences.IgnoreAttribute) || x.AttributeClass.ApproximatelyEqual(typeReferencesIgnoreDataMemberAttribute));
+ }))
+ {
+ continue;
+ }
+
+ var isReadable = item.GetMethod != null && item.GetMethod.DeclaredAccessibility == Accessibility.Public && !item.IsStatic;
+ var isWritable = item.SetMethod != null && item.SetMethod.DeclaredAccessibility == Accessibility.Public && !item.IsStatic;
+ if (!isReadable && !isWritable)
+ {
+ continue;
+ }
+
+ var customFormatterAttr = item.GetAttributes().FirstOrDefault(x => x.AttributeClass.ApproximatelyEqual(this.typeReferences.MessagePackFormatterAttribute))?.ConstructorArguments[0].Value as INamedTypeSymbol;
+ var key = item.GetAttributes().FirstOrDefault(x => x.AttributeClass.ApproximatelyEqual(this.typeReferences.KeyAttribute))?.ConstructorArguments[0]
+ ?? throw new MessagePackGeneratorResolveFailedException("all public members must mark KeyAttribute or IgnoreMemberAttribute." + " type: " + type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) + " member:" + item.Name);
+
+ var intKey = key is { Value: int intKeyValue } ? intKeyValue : default(int?);
+ var stringKey = key is { Value: string stringKeyValue } ? stringKeyValue : default;
+ if (intKey == null && stringKey == null)
+ {
+ throw new MessagePackGeneratorResolveFailedException("both IntKey and StringKey are null." + " type: " + type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) + " member:" + item.Name);
+ }
+
+ if (searchFirst)
+ {
+ searchFirst = false;
+ isIntKey = intKey != null;
+ }
+ else
+ {
+ if ((isIntKey && intKey == null) || (!isIntKey && stringKey == null))
+ {
+ throw new MessagePackGeneratorResolveFailedException("all members key type must be same." + " type: " + type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) + " member:" + item.Name);
+ }
+ }
+
+ if (isIntKey)
+ {
+ if (intMembers.ContainsKey(intKey!.Value))
+ {
+ throw new MessagePackGeneratorResolveFailedException("key is duplicated, all members key must be unique." + " type: " + type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) + " member:" + item.Name);
+ }
+
+ var member = new MemberSerializationInfo(true, isWritable, isReadable, intKey!.Value, item.Name, item.Name, item.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), item.Type.ToDisplayString(BinaryWriteFormat), customFormatterAttr?.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat));
+ intMembers.Add(member.IntKey, member);
+ }
+ else
+ {
+ if (stringMembers.ContainsKey(stringKey!))
+ {
+ throw new MessagePackGeneratorResolveFailedException("key is duplicated, all members key must be unique." + " type: " + type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) + " member:" + item.Name);
+ }
+
+ var member = new MemberSerializationInfo(true, isWritable, isReadable, hiddenIntKey++, stringKey!, item.Name, item.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), item.Type.ToDisplayString(BinaryWriteFormat), customFormatterAttr?.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat));
+ stringMembers.Add(member.StringKey, member);
+ }
+
+ var messagePackFormatter = item.GetAttributes().FirstOrDefault(x => x.AttributeClass.ApproximatelyEqual(this.typeReferences.MessagePackFormatterAttribute))?.ConstructorArguments[0];
+
+ if (messagePackFormatter == null)
+ {
+ this.CollectCore(item.Type); // recursive collect
+ }
+ }
+
+ foreach (IFieldSymbol item in type.GetAllMembers().OfType())
+ {
+ if (item.IsImplicitlyDeclared)
+ {
+ continue;
+ }
+
+ if (item.GetAttributes().Any(x => x.AttributeClass.ApproximatelyEqual(this.typeReferences.IgnoreAttribute)))
+ {
+ continue;
+ }
+
+ var isReadable = item.DeclaredAccessibility == Accessibility.Public && !item.IsStatic;
+ var isWritable = item.DeclaredAccessibility == Accessibility.Public && !item.IsReadOnly && !item.IsStatic;
+ if (!isReadable && !isWritable)
+ {
+ continue;
+ }
+
+ var customFormatterAttr = item.GetAttributes().FirstOrDefault(x => x.AttributeClass.ApproximatelyEqual(this.typeReferences.MessagePackFormatterAttribute))?.ConstructorArguments[0].Value as INamedTypeSymbol;
+ var key = item.GetAttributes().FirstOrDefault(x => x.AttributeClass.ApproximatelyEqual(this.typeReferences.KeyAttribute))?.ConstructorArguments[0]
+ ?? throw new MessagePackGeneratorResolveFailedException("all public members must mark KeyAttribute or IgnoreMemberAttribute." + " type: " + type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) + " member:" + item.Name);
+
+ var intKey = key is { Value: int intKeyValue } ? intKeyValue : default(int?);
+ var stringKey = key is { Value: string stringKeyValue } ? stringKeyValue : default;
+ if (intKey == null && stringKey == null)
+ {
+ throw new MessagePackGeneratorResolveFailedException("both IntKey and StringKey are null." + " type: " + type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) + " member:" + item.Name);
+ }
+
+ if (searchFirst)
+ {
+ searchFirst = false;
+ isIntKey = intKey != null;
+ }
+ else
+ {
+ if ((isIntKey && intKey == null) || (!isIntKey && stringKey == null))
+ {
+ throw new MessagePackGeneratorResolveFailedException("all members key type must be same." + " type: " + type.Name + " member:" + item.Name);
+ }
+ }
+
+ if (isIntKey)
+ {
+ if (intMembers.ContainsKey(intKey!.Value))
+ {
+ throw new MessagePackGeneratorResolveFailedException("key is duplicated, all members key must be unique." + " type: " + type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) + " member:" + item.Name);
+ }
+
+ var member = new MemberSerializationInfo(true, isWritable, isReadable, intKey!.Value, item.Name, item.Name, item.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), item.Type.ToDisplayString(BinaryWriteFormat), customFormatterAttr?.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat));
+ intMembers.Add(member.IntKey, member);
+ }
+ else
+ {
+ if (stringMembers.ContainsKey(stringKey!))
+ {
+ throw new MessagePackGeneratorResolveFailedException("key is duplicated, all members key must be unique." + " type: " + type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) + " member:" + item.Name);
+ }
+
+ var member = new MemberSerializationInfo(true, isWritable, isReadable, hiddenIntKey++, stringKey!, item.Name, item.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), item.Type.ToDisplayString(BinaryWriteFormat), customFormatterAttr?.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat));
+ stringMembers.Add(member.StringKey, member);
+ }
+
+ this.CollectCore(item.Type); // recursive collect
+ }
+ }
+
+ // GetConstructor
+ var ctorEnumerator = default(IEnumerator);
+ var ctor = type.Constructors.Where(x => x.DeclaredAccessibility == Accessibility.Public).SingleOrDefault(x => x.GetAttributes().Any(y => y.AttributeClass != null && y.AttributeClass.ApproximatelyEqual(this.typeReferences.SerializationConstructorAttribute)));
+ if (ctor == null)
+ {
+ ctorEnumerator = type.Constructors.Where(x => x.DeclaredAccessibility == Accessibility.Public).OrderByDescending(x => x.Parameters.Length).GetEnumerator();
+
+ if (ctorEnumerator.MoveNext())
+ {
+ ctor = ctorEnumerator.Current;
+ }
+ }
+
+ // struct allows null ctor
+ if (ctor == null && isClass)
+ {
+ throw new MessagePackGeneratorResolveFailedException("can't find public constructor. type:" + type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat));
+ }
+
+ var constructorParameters = new List();
+ if (ctor != null)
+ {
+ var constructorLookupDictionary = stringMembers.ToLookup(x => x.Key, x => x, StringComparer.OrdinalIgnoreCase);
+ do
+ {
+ constructorParameters.Clear();
+ var ctorParamIndex = 0;
+ foreach (IParameterSymbol item in ctor!.Parameters)
+ {
+ MemberSerializationInfo paramMember;
+ if (isIntKey)
+ {
+ if (intMembers.TryGetValue(ctorParamIndex, out paramMember!))
+ {
+ if (item.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) == paramMember.Type && paramMember.IsReadable)
+ {
+ constructorParameters.Add(paramMember);
+ }
+ else
+ {
+ if (ctorEnumerator != null)
+ {
+ ctor = null;
+ continue;
+ }
+ else
+ {
+ throw new MessagePackGeneratorResolveFailedException("can't find matched constructor parameter, parameterType mismatch. type:" + type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) + " parameterIndex:" + ctorParamIndex + " parameterType:" + item.Type.Name);
+ }
+ }
+ }
+ else
+ {
+ if (ctorEnumerator != null)
+ {
+ ctor = null;
+ continue;
+ }
+ else
+ {
+ throw new MessagePackGeneratorResolveFailedException("can't find matched constructor parameter, index not found. type:" + type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) + " parameterIndex:" + ctorParamIndex);
+ }
+ }
+ }
+ else
+ {
+ IEnumerable> hasKey = constructorLookupDictionary[item.Name];
+ using var enumerator = hasKey.GetEnumerator();
+
+ // hasKey.Count() == 0
+ if (!enumerator.MoveNext())
+ {
+ if (ctorEnumerator == null)
+ {
+ throw new MessagePackGeneratorResolveFailedException("can't find matched constructor parameter, index not found. type:" + type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) + " parameterName:" + item.Name);
+ }
+
+ ctor = null;
+ continue;
+ }
+
+ var first = enumerator.Current.Value;
+
+ // hasKey.Count() != 1
+ if (enumerator.MoveNext())
+ {
+ if (ctorEnumerator == null)
+ {
+ throw new MessagePackGeneratorResolveFailedException("duplicate matched constructor parameter name:" + type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) + " parameterName:" + item.Name + " parameterType:" + item.Type.Name);
+ }
+
+ ctor = null;
+ continue;
+ }
+
+ paramMember = first;
+ if (item.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) == paramMember.Type && paramMember.IsReadable)
+ {
+ constructorParameters.Add(paramMember);
+ }
+ else
+ {
+ if (ctorEnumerator == null)
+ {
+ throw new MessagePackGeneratorResolveFailedException("can't find matched constructor parameter, parameterType mismatch. type:" + type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) + " parameterName:" + item.Name + " parameterType:" + item.Type.Name);
+ }
+
+ ctor = null;
+ continue;
+ }
+ }
+
+ ctorParamIndex++;
+ }
+ }
+ while (TryGetNextConstructor(ctorEnumerator, ref ctor));
+
+ if (ctor == null)
+ {
+ throw new MessagePackGeneratorResolveFailedException("can't find matched constructor. type:" + type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat));
+ }
+ }
+
+ var hasSerializationConstructor = type.AllInterfaces.Any(x => x.ApproximatelyEqual(this.typeReferences.IMessagePackSerializationCallbackReceiver));
+ var needsCastOnBefore = true;
+ var needsCastOnAfter = true;
+ if (hasSerializationConstructor)
+ {
+ needsCastOnBefore = !type.GetMembers("OnBeforeSerialize").Any();
+ needsCastOnAfter = !type.GetMembers("OnAfterDeserialize").Any();
+ }
+
+ var info = new ObjectSerializationInfo(isClass, isOpenGenericType, isOpenGenericType ? type.TypeParameters.Select(ToGenericTypeParameterInfo).ToArray() : Array.Empty(), constructorParameters.ToArray(), isIntKey, isIntKey ? intMembers.Values.ToArray() : stringMembers.Values.ToArray(), isOpenGenericType ? GetGenericFormatterClassName(type) : GetMinimallyQualifiedClassName(type), type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), type.ContainingNamespace.IsGlobalNamespace ? null : type.ContainingNamespace.ToDisplayString(), hasSerializationConstructor, needsCastOnAfter, needsCastOnBefore);
+
+ return info;
+ }
+
+ private static GenericTypeParameterInfo ToGenericTypeParameterInfo(ITypeParameterSymbol typeParameter)
+ {
+ var constraints = new List();
+
+ // `notnull`, `unmanaged`, `class`, `struct` constraint must come before any constraints.
+ if (typeParameter.HasNotNullConstraint)
+ {
+ constraints.Add("notnull");
+ }
+
+ if (typeParameter.HasReferenceTypeConstraint)
+ {
+ constraints.Add(typeParameter.ReferenceTypeConstraintNullableAnnotation == NullableAnnotation.Annotated ? "class?" : "class");
+ }
+
+ if (typeParameter.HasValueTypeConstraint)
+ {
+ constraints.Add(typeParameter.HasUnmanagedTypeConstraint ? "unmanaged" : "struct");
+ }
+
+ // constraint types (IDisposable, IEnumerable ...)
+ foreach (var t in typeParameter.ConstraintTypes)
+ {
+ var constraintTypeFullName = t.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat.AddMiscellaneousOptions(SymbolDisplayMiscellaneousOptions.IncludeNullableReferenceTypeModifier));
+ constraints.Add(constraintTypeFullName);
+ }
+
+ // `new()` constraint must be last in constraints.
+ if (typeParameter.HasConstructorConstraint)
+ {
+ constraints.Add("new()");
+ }
+
+ return new GenericTypeParameterInfo(typeParameter.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), string.Join(", ", constraints));
+ }
+
+ private static string GetGenericFormatterClassName(INamedTypeSymbol type)
+ {
+ return type.Name;
+ }
+
+ private static string GetMinimallyQualifiedClassName(INamedTypeSymbol type)
+ {
+ var name = type.ContainingType is object ? GetMinimallyQualifiedClassName(type.ContainingType) + "_" : string.Empty;
+ name += type.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat);
+ name = name.Replace('.', '_');
+ name = name.Replace('<', '_');
+ name = name.Replace('>', '_');
+ name = Regex.Replace(name, @"\[([,])*\]", match => $"Array{match.Length - 1}");
+ name = name.Replace("?", string.Empty);
+ return name;
+ }
+
+ private static bool TryGetNextConstructor(IEnumerator? ctorEnumerator, ref IMethodSymbol? ctor)
+ {
+ if (ctorEnumerator == null || ctor != null)
+ {
+ return false;
+ }
+
+ if (ctorEnumerator.MoveNext())
+ {
+ ctor = ctorEnumerator.Current;
+ return true;
+ }
+ else
+ {
+ ctor = null;
+ return false;
+ }
+ }
+
+ private bool IsAllowAccessibility(ITypeSymbol symbol)
+ {
+ do
+ {
+ if (symbol.DeclaredAccessibility != Accessibility.Public)
+ {
+ if (this.disallowInternal)
+ {
+ return false;
+ }
+
+ if (symbol.DeclaredAccessibility != Accessibility.Internal)
+ {
+ return true;
+ }
+ }
+
+ symbol = symbol.ContainingType;
+ }
+ while (symbol != null);
+
+ return true;
+ }
+
+ private bool IsOpenGenericTypeRecursively(INamedTypeSymbol type)
+ {
+ return type.IsGenericType && type.TypeArguments.Any(x => x is ITypeParameterSymbol || (x is INamedTypeSymbol symbol && IsOpenGenericTypeRecursively(symbol)));
+ }
+}
diff --git a/src/MessagePack.Generator/CodeGenerator.cs b/src/MessagePack.Generator/CodeGenerator.cs
new file mode 100644
index 000000000..9fce4c4bb
--- /dev/null
+++ b/src/MessagePack.Generator/CodeGenerator.cs
@@ -0,0 +1,312 @@
+// Copyright (c) All contributors. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System.Diagnostics;
+using System.Text;
+using MessagePack.Generator.CodeAnalysis;
+using MessagePack.Generator.Transforms;
+using Microsoft.CodeAnalysis;
+
+namespace MessagePack.Generator;
+
+public class CodeGenerator
+{
+ private static readonly HashSet InvalidFileCharSet = new(Path.GetInvalidFileNameChars());
+
+ private static readonly Encoding NoBomUtf8 = new UTF8Encoding(false);
+
+ private readonly Action logger;
+
+ public CodeGenerator(Action logger, CancellationToken cancellationToken)
+ {
+ this.logger = logger;
+ }
+
+ ///
+ /// Generates the specialized resolver and formatters for the types that require serialization in a given compilation.
+ ///
+ /// The compilation to read types from as an input to code generation.
+ /// The name of the generated source file.
+ /// The resolver name.
+ /// The namespace for the generated type to be created in. May be null.
+ /// A boolean value that indicates whether all formatters should use property maps instead of more compact arrays.
+ /// A comma-delimited list of symbols that should surround redundant generated files. May be null.
+ /// May be null.
+ /// A task that indicates when generation has completed.
+ public async Task GenerateFileAsync(
+ Compilation compilation,
+ string output,
+ string resolverName,
+ string? @namespace,
+ bool useMapMode,
+ string? multipleIfDirectiveOutputSymbols,
+ string[]? externalIgnoreTypeNames)
+ {
+ var namespaceDot = string.IsNullOrWhiteSpace(@namespace) ? string.Empty : @namespace + ".";
+ var multipleOutputSymbols = multipleIfDirectiveOutputSymbols?.Split(',') ?? Array.Empty();
+
+ var sw = Stopwatch.StartNew();
+
+ 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, Console.WriteLine);
+
+ logger("Project Compilation Complete:" + sw.Elapsed.ToString());
+
+ sw.Restart();
+ logger("Method Collect Start");
+
+ var (objectInfo, enumInfo, genericInfo, unionInfo) = collector.Collect();
+
+ logger("Method Collect Complete:" + sw.Elapsed.ToString());
+
+ logger("Output Generation Start");
+ sw.Restart();
+
+ if (Path.GetExtension(output) == ".cs")
+ {
+ // SingleFile Output
+ 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);
+ }
+
+ if (objectInfo.Length == 0 && enumInfo.Length == 0 && genericInfo.Length == 0 && unionInfo.Length == 0)
+ {
+ logger("Generated result is empty, unexpected result?");
+ }
+ }
+
+ 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 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(IGrouping x)
+ {
+ if (x.Key == null)
+ {
+ return namespaceDot + "Formatters";
+ }
+
+ return namespaceDot + "Formatters." + x.Key;
+ }
+
+ var enumFormatterTemplates = enumInfo
+ .GroupBy(x => x.Namespace)
+ .Select(x => new EnumTemplate(GetNamespace(x), x.ToArray()))
+ .ToArray();
+
+ var unionFormatterTemplates = unionInfo
+ .GroupBy(x => x.Namespace)
+ .Select(x => new UnionTemplate(GetNamespace(x), x.ToArray()))
+ .ToArray();
+
+ var resolverTemplate = new ResolverTemplate(namespaceDot + "Resolvers", namespaceDot + "Formatters", resolverName, 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)
+ {
+ 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().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)
+ {
+ builder.Append(Path.DirectorySeparatorChar);
+ }
+ }
+ }
+
+ void AppendChar(char c)
+ {
+ if (c == '.' || InvalidFileCharSet.Contains(c))
+ {
+ builder.Append('_');
+ }
+ else
+ {
+ builder.Append(c);
+ }
+ }
+
+ void Append(string text)
+ {
+ var span = text.AsSpan();
+ while (!span.IsEmpty)
+ {
+ var index = span.IndexOf("global::".AsSpan());
+ if (index == -1)
+ {
+ foreach (var c in span)
+ {
+ AppendChar(c);
+ }
+
+ break;
+ }
+
+ if (index == 0)
+ {
+ span = span.Slice("global::".Length);
+ continue;
+ }
+
+ foreach (var c in span.Slice(0, index))
+ {
+ AppendChar(c);
+ }
+
+ span = span.Slice(index + "global::".Length);
+ }
+ }
+
+ AppendDir(dir);
+
+ if (!string.IsNullOrWhiteSpace(multipleOutSymbol))
+ {
+ text = $"#if {multipleOutSymbol}" + Environment.NewLine + text + Environment.NewLine + "#endif";
+ AppendDir(MultiSymbolToSafeFilePath(multipleOutSymbol));
+ }
+
+ Append(ns);
+ builder.Append('_');
+ Append(name);
+ builder.Append(".cs");
+
+ return OutputAsync(builder.ToString(), text);
+ }
+
+ private Task OutputAsync(string path, string text)
+ {
+ path = path.Replace("global::", string.Empty);
+
+ const string prefix = "[Out]";
+ logger(prefix + path);
+
+ var fi = new FileInfo(path);
+ if (fi.Directory != null && !fi.Directory.Exists)
+ {
+ fi.Directory.Create();
+ }
+
+ File.WriteAllText(path, NormalizeNewLines(text), NoBomUtf8);
+ return Task.CompletedTask;
+ }
+
+ private static string MultiSymbolToSafeFilePath(string symbol)
+ {
+ return symbol.Replace("!", "NOT_").Replace("(", string.Empty).Replace(")", string.Empty).Replace("||", "_OR_").Replace("&&", "_AND_");
+ }
+
+ private static string NormalizeNewLines(string content)
+ {
+ // The T4 generated code may be text with mixed line ending types. (CR + CRLF)
+ // We need to normalize the line ending type in each Operating Systems. (e.g. Windows=CRLF, Linux/macOS=LF)
+ return content.Replace("\r\n", "\n").Replace("\n", Environment.NewLine);
+ }
+}
diff --git a/src/MessagePack.Generator/MessagePack.Generator.csproj b/src/MessagePack.Generator/MessagePack.Generator.csproj
index b350ee8fb..5d97f41ba 100644
--- a/src/MessagePack.Generator/MessagePack.Generator.csproj
+++ b/src/MessagePack.Generator/MessagePack.Generator.csproj
@@ -5,26 +5,81 @@
11
enable
enable
- true
cs
MessagePack.Generator
MessagePack Code Generator
- MessagePack standalone code generator.
+ MessagePack C# source generator.
MsgPack;MessagePack;Serialization;Formatter;Serializer;Unity;Xamarin
-
-
-
-
-
+
+
-
+
+
+
+
+ True
+ True
+ EnumTemplate.tt
+
+
+ True
+ True
+ FormatterTemplate.tt
+
+
+ True
+ True
+ ResolverTemplate.tt
+
+
+ True
+ True
+ StringKeyFormatterTemplate.tt
+
+
+ %(FileName).tt
+ True
+ True
+
+
+ True
+ True
+ UnionTemplate.tt
+
+
+
+
+
+ EnumTemplate.cs
+ TextTemplatingFilePreprocessor
+
+
+ FormatterTemplate.cs
+ TextTemplatingFilePreprocessor
+
+
+ ResolverTemplate.cs
+ TextTemplatingFilePreprocessor
+
+
+ StringKeyFormatterTemplate.cs
+ TextTemplatingFilePreprocessor
+ MessagePack.Generator.Transforms
+
+
+ UnionTemplate.cs
+ TextTemplatingFilePreprocessor
+
+
+
+
diff --git a/src/MessagePack.Generator/MessagepackGenerator.Emit.cs b/src/MessagePack.Generator/MessagepackGenerator.Emit.cs
index 01a2dd35a..11c02d9cf 100644
--- a/src/MessagePack.Generator/MessagepackGenerator.Emit.cs
+++ b/src/MessagePack.Generator/MessagepackGenerator.Emit.cs
@@ -2,8 +2,8 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System.Text;
-using MessagePackCompiler.CodeAnalysis;
-using MessagePackCompiler.Generator;
+using MessagePack.Generator.CodeAnalysis;
+using MessagePack.Generator.Transforms;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
diff --git a/src/MessagePack.GeneratorCore/Generator/EnumTemplate.cs b/src/MessagePack.Generator/Transforms/EnumTemplate.cs
similarity index 98%
rename from src/MessagePack.GeneratorCore/Generator/EnumTemplate.cs
rename to src/MessagePack.Generator/Transforms/EnumTemplate.cs
index 382112a3b..d7e00e617 100644
--- a/src/MessagePack.GeneratorCore/Generator/EnumTemplate.cs
+++ b/src/MessagePack.Generator/Transforms/EnumTemplate.cs
@@ -7,7 +7,7 @@
// the code is regenerated.
//
// ------------------------------------------------------------------------------
-namespace MessagePackCompiler.Generator
+namespace MessagePack.Generator.Transforms
{
using System.Linq;
using System.Text;
@@ -26,8 +26,6 @@ public partial class EnumTemplate : EnumTemplateBase
public virtual string TransformText()
{
this.Write(@"//
-// THIS (.cs) FILE IS GENERATED BY MPC(MessagePack-CSharp). DO NOT CHANGE IT.
-//
#pragma warning disable 618
#pragma warning disable 612
@@ -93,7 +91,7 @@ public class EnumTemplateBase
///
/// The string builder that generation-time code is using to assemble generated output
///
- protected System.Text.StringBuilder GenerationEnvironment
+ public System.Text.StringBuilder GenerationEnvironment
{
get
{
diff --git a/src/MessagePack.GeneratorCore/Generator/EnumTemplate.tt b/src/MessagePack.Generator/Transforms/EnumTemplate.tt
similarity index 94%
rename from src/MessagePack.GeneratorCore/Generator/EnumTemplate.tt
rename to src/MessagePack.Generator/Transforms/EnumTemplate.tt
index 74214c6c1..825902caf 100644
--- a/src/MessagePack.GeneratorCore/Generator/EnumTemplate.tt
+++ b/src/MessagePack.Generator/Transforms/EnumTemplate.tt
@@ -4,8 +4,6 @@
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
//
-// THIS (.cs) FILE IS GENERATED BY MPC(MessagePack-CSharp). DO NOT CHANGE IT.
-//
#pragma warning disable 618
#pragma warning disable 612
diff --git a/src/MessagePack.GeneratorCore/Generator/FormatterTemplate.cs b/src/MessagePack.Generator/Transforms/FormatterTemplate.cs
similarity index 99%
rename from src/MessagePack.GeneratorCore/Generator/FormatterTemplate.cs
rename to src/MessagePack.Generator/Transforms/FormatterTemplate.cs
index 396d6a52d..0c8a8940f 100644
--- a/src/MessagePack.GeneratorCore/Generator/FormatterTemplate.cs
+++ b/src/MessagePack.Generator/Transforms/FormatterTemplate.cs
@@ -7,7 +7,7 @@
// the code is regenerated.
//
// ------------------------------------------------------------------------------
-namespace MessagePackCompiler.Generator
+namespace MessagePack.Generator.Transforms
{
using System.Linq;
using System.Text;
@@ -26,8 +26,6 @@ public partial class FormatterTemplate : FormatterTemplateBase
public virtual string TransformText()
{
this.Write(@"//
-// THIS (.cs) FILE IS GENERATED BY MPC(MessagePack-CSharp). DO NOT CHANGE IT.
-//
#pragma warning disable 618
#pragma warning disable 612
@@ -242,7 +240,7 @@ public class FormatterTemplateBase
///
/// The string builder that generation-time code is using to assemble generated output
///
- protected System.Text.StringBuilder GenerationEnvironment
+ public System.Text.StringBuilder GenerationEnvironment
{
get
{
diff --git a/src/MessagePack.GeneratorCore/Generator/FormatterTemplate.tt b/src/MessagePack.Generator/Transforms/FormatterTemplate.tt
similarity index 98%
rename from src/MessagePack.GeneratorCore/Generator/FormatterTemplate.tt
rename to src/MessagePack.Generator/Transforms/FormatterTemplate.tt
index 240b107fc..adbd7faa1 100644
--- a/src/MessagePack.GeneratorCore/Generator/FormatterTemplate.tt
+++ b/src/MessagePack.Generator/Transforms/FormatterTemplate.tt
@@ -4,8 +4,6 @@
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
//
-// THIS (.cs) FILE IS GENERATED BY MPC(MessagePack-CSharp). DO NOT CHANGE IT.
-//
#pragma warning disable 618
#pragma warning disable 612
diff --git a/src/MessagePack.Generator/Transforms/IFormatterTemplate.cs b/src/MessagePack.Generator/Transforms/IFormatterTemplate.cs
new file mode 100644
index 000000000..e76860c13
--- /dev/null
+++ b/src/MessagePack.Generator/Transforms/IFormatterTemplate.cs
@@ -0,0 +1,15 @@
+// Copyright (c) All contributors. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using MessagePack.Generator.CodeAnalysis;
+
+namespace MessagePack.Generator.Transforms;
+
+public interface IFormatterTemplate
+{
+ string Namespace { get; }
+
+ ObjectSerializationInfo[] ObjectSerializationInfos { get; }
+
+ string TransformText();
+}
diff --git a/src/MessagePack.GeneratorCore/Generator/ResolverTemplate.cs b/src/MessagePack.Generator/Transforms/ResolverTemplate.cs
similarity index 98%
rename from src/MessagePack.GeneratorCore/Generator/ResolverTemplate.cs
rename to src/MessagePack.Generator/Transforms/ResolverTemplate.cs
index 58f207114..f817bdd8f 100644
--- a/src/MessagePack.GeneratorCore/Generator/ResolverTemplate.cs
+++ b/src/MessagePack.Generator/Transforms/ResolverTemplate.cs
@@ -7,7 +7,7 @@
// the code is regenerated.
//
// ------------------------------------------------------------------------------
-namespace MessagePackCompiler.Generator
+namespace MessagePack.Generator.Transforms
{
using System.Linq;
using System.Text;
@@ -26,8 +26,6 @@ public partial class ResolverTemplate : ResolverTemplateBase
public virtual string TransformText()
{
this.Write(@"//
-// THIS (.cs) FILE IS GENERATED BY MPC(MessagePack-CSharp). DO NOT CHANGE IT.
-//
#pragma warning disable 618
#pragma warning disable 612
@@ -146,7 +144,7 @@ public class ResolverTemplateBase
///
/// The string builder that generation-time code is using to assemble generated output
///
- protected System.Text.StringBuilder GenerationEnvironment
+ public System.Text.StringBuilder GenerationEnvironment
{
get
{
diff --git a/src/MessagePack.GeneratorCore/Generator/ResolverTemplate.tt b/src/MessagePack.Generator/Transforms/ResolverTemplate.tt
similarity index 96%
rename from src/MessagePack.GeneratorCore/Generator/ResolverTemplate.tt
rename to src/MessagePack.Generator/Transforms/ResolverTemplate.tt
index 6837185df..f634c97d8 100644
--- a/src/MessagePack.GeneratorCore/Generator/ResolverTemplate.tt
+++ b/src/MessagePack.Generator/Transforms/ResolverTemplate.tt
@@ -4,8 +4,6 @@
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
//
-// THIS (.cs) FILE IS GENERATED BY MPC(MessagePack-CSharp). DO NOT CHANGE IT.
-//
#pragma warning disable 618
#pragma warning disable 612
diff --git a/src/MessagePack.Generator/Transforms/ShouldUseFormatterResolverHelper.cs b/src/MessagePack.Generator/Transforms/ShouldUseFormatterResolverHelper.cs
new file mode 100644
index 000000000..f9d718734
--- /dev/null
+++ b/src/MessagePack.Generator/Transforms/ShouldUseFormatterResolverHelper.cs
@@ -0,0 +1,46 @@
+// Copyright (c) All contributors. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using MessagePack.Generator.CodeAnalysis;
+
+namespace MessagePack.Generator.Transforms;
+
+public static class ShouldUseFormatterResolverHelper
+{
+ ///
+ /// Keep this list in sync with DynamicObjectTypeBuilder.IsOptimizeTargetType.
+ ///
+ internal static readonly string[] PrimitiveTypes =
+ {
+ "short",
+ "int",
+ "long",
+ "ushort",
+ "uint",
+ "ulong",
+ "float",
+ "double",
+ "bool",
+ "byte",
+ "sbyte",
+ "char",
+ "byte[]",
+
+ // Do not include types that resolvers are allowed to modify.
+ ////"global::System.DateTime", // OldSpec has no support, so for that and perf reasons a .NET native DateTime resolver exists.
+ ////"string", // https://github.com/Cysharp/MasterMemory provides custom formatter for string interning.
+ };
+
+ public static bool ShouldUseFormatterResolver(MemberSerializationInfo[] infos)
+ {
+ foreach (var memberSerializationInfo in infos)
+ {
+ if (memberSerializationInfo.CustomFormatterTypeName == null && Array.IndexOf(PrimitiveTypes, memberSerializationInfo.Type) == -1)
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+}
diff --git a/src/MessagePack.Generator/Transforms/StringKey/EmbedStringHelper.cs b/src/MessagePack.Generator/Transforms/StringKey/EmbedStringHelper.cs
new file mode 100644
index 000000000..40b879b2e
--- /dev/null
+++ b/src/MessagePack.Generator/Transforms/StringKey/EmbedStringHelper.cs
@@ -0,0 +1,85 @@
+// Copyright (c) All contributors. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System.Text;
+
+namespace MessagePack.Generator.Transforms;
+
+public static class EmbedStringHelper
+{
+ public static readonly Encoding Utf8 = new UTF8Encoding(false);
+
+ public static string ToByteArrayString(byte[] binary)
+ {
+ var headerLength = GetHeaderLength(binary.Length);
+ Span header = stackalloc byte[headerLength];
+ EmbedHeader(binary.Length, header);
+ var buffer = new StringBuilder().Append("new byte[").Append(headerLength).Append(" + ").Append(binary.Length).Append("] { ").Append(header[0]);
+ foreach (var b in header.Slice(1))
+ {
+ buffer.Append(", ").Append(b);
+ }
+
+ foreach (var b in binary)
+ {
+ buffer.Append(", ").Append(b);
+ }
+
+ return buffer.Append(" }").ToString();
+ }
+
+ public static int GetHeaderLength(int byteCount)
+ {
+ if (byteCount <= 31)
+ {
+ return 1;
+ }
+
+ if (byteCount <= byte.MaxValue)
+ {
+ return 2;
+ }
+
+ return byteCount <= ushort.MaxValue ? 3 : 5;
+ }
+
+ public static void EmbedHeader(int byteCount, Span destination)
+ {
+ if (byteCount <= 31)
+ {
+ destination[0] = (byte)(0xa0 | byteCount);
+ return;
+ }
+
+ if (byteCount <= byte.MaxValue)
+ {
+ destination[0] = 0xd9;
+ destination[1] = unchecked((byte)byteCount);
+ return;
+ }
+
+ if (byteCount <= ushort.MaxValue)
+ {
+ destination[0] = 0xda;
+ destination[1] = unchecked((byte)(byteCount >> 8));
+ destination[2] = unchecked((byte)byteCount);
+ return;
+ }
+
+ destination[0] = 0xdb;
+ destination[1] = unchecked((byte)(byteCount >> 24));
+ destination[2] = unchecked((byte)(byteCount >> 16));
+ destination[3] = unchecked((byte)(byteCount >> 8));
+ destination[4] = unchecked((byte)byteCount);
+ }
+
+ public static byte[] GetEncodedStringBytes(string value)
+ {
+ var byteCount = Utf8.GetByteCount(value);
+ var headerLength = GetHeaderLength(byteCount);
+ var bytes = new byte[headerLength + byteCount];
+ EmbedHeader(byteCount, bytes);
+ Utf8.GetBytes(value, 0, value.Length, bytes, headerLength);
+ return bytes;
+ }
+}
diff --git a/src/MessagePack.Generator/Transforms/StringKey/StringKeyFormatterDeserializeHelper.cs b/src/MessagePack.Generator/Transforms/StringKey/StringKeyFormatterDeserializeHelper.cs
new file mode 100644
index 000000000..7801b06eb
--- /dev/null
+++ b/src/MessagePack.Generator/Transforms/StringKey/StringKeyFormatterDeserializeHelper.cs
@@ -0,0 +1,248 @@
+// Copyright (c) All contributors. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System.Text;
+using MessagePack.Generator.CodeAnalysis;
+using MessagePack.Internal;
+
+namespace MessagePack.Generator.Transforms;
+
+internal static class StringKeyFormatterDeserializeHelper
+{
+ public static string Classify(ObjectSerializationInfo objectSerializationInfo, string indent, bool canOverwrite)
+ {
+ var memberArray = objectSerializationInfo.Members;
+ var buffer = new StringBuilder();
+ foreach (var memberInfoTuples in memberArray.Select(member => new MemberInfoTuple(member, IsConstructorParameter(objectSerializationInfo, member))).GroupBy(member => member.Binary.Length))
+ {
+ var binaryLength = memberInfoTuples.Key;
+ var keyLength = binaryLength >> 3;
+ keyLength += keyLength << 3 == binaryLength ? 0 : 1;
+
+ buffer.Append(indent).Append("case ").Append(binaryLength).Append(":\r\n");
+ ClassifyRecursion(buffer, indent, 1, keyLength, memberInfoTuples, canOverwrite);
+ }
+
+ return buffer.ToString();
+ }
+
+ private static bool IsConstructorParameter(ObjectSerializationInfo objectSerializationInfo, MemberSerializationInfo member)
+ {
+ foreach (var parameter in objectSerializationInfo.ConstructorParameters)
+ {
+ if (parameter.Equals(member))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private static void Assign(StringBuilder buffer, in MemberInfoTuple member, bool canOverwrite, string indent, string tab, int tabCount)
+ {
+ if (member.Info.IsWritable || member.IsConstructorParameter)
+ {
+ if (canOverwrite)
+ {
+ buffer.Append("____result.").Append(member.Info.Name).Append(" = ");
+ }
+ else
+ {
+ if (!member.IsConstructorParameter)
+ {
+ buffer.Append("__").Append(member.Info.Name).Append("__IsInitialized = true;\r\n").Append(indent);
+ for (var i = 0; i < tabCount; i++)
+ {
+ buffer.Append(tab);
+ }
+ }
+
+ buffer.Append("__").Append(member.Info.Name).Append("__ = ");
+ }
+
+ buffer.Append(member.Info.GetDeserializeMethodString()).Append(";\r\n");
+ }
+ else
+ {
+ buffer.Append("reader.Skip();\r\n");
+ }
+ }
+
+ private static void ClassifyRecursion(StringBuilder buffer, string indent, int tabCount, int keyLength, IEnumerable memberCollection, bool canOverwrite)
+ {
+ const string Tab = " ";
+ buffer.Append(indent);
+ for (var i = 0; i < tabCount; i++)
+ {
+ buffer.Append(Tab);
+ }
+
+ var memberArray = memberCollection.ToArray();
+ if (memberArray.Length == 1)
+ {
+ var member = memberArray[0];
+ EmbedOne(buffer, indent, tabCount, member, canOverwrite);
+ return;
+ }
+
+ buffer.Append("switch (global::MessagePack.Internal.AutomataKeyGen.GetKey(ref stringKey))\r\n").Append(indent);
+ for (var i = 0; i < tabCount; i++)
+ {
+ buffer.Append(Tab);
+ }
+
+ buffer.Append("{\r\n" + Tab).Append(indent);
+ for (var i = 0; i < tabCount; i++)
+ {
+ buffer.Append(Tab);
+ }
+
+ buffer.Append("default: goto FAIL;");
+
+ foreach (var grouping in memberArray.GroupBy(member => member.Key[tabCount - 1]))
+ {
+ buffer.Append("\r\n" + Tab).Append(indent);
+ for (var i = 0; i < tabCount; i++)
+ {
+ buffer.Append(Tab);
+ }
+
+ buffer.Append("case ").Append(grouping.Key).Append("UL:\r\n");
+
+ if (tabCount == keyLength)
+ {
+ buffer.Append(Tab + Tab).Append(indent);
+ for (var i = 0; i < tabCount; i++)
+ {
+ buffer.Append(Tab);
+ }
+
+ var member = grouping.Single();
+ Assign(buffer, member, canOverwrite, indent, Tab, tabCount + 2);
+ buffer.Append(Tab + Tab).Append(indent);
+ for (var i = 0; i < tabCount; i++)
+ {
+ buffer.Append(Tab);
+ }
+
+ buffer.Append("continue;");
+ continue;
+ }
+
+ ClassifyRecursion(buffer, indent + Tab, tabCount + 1, keyLength, grouping, canOverwrite);
+ }
+
+ buffer.Append("\r\n").Append(indent);
+ for (var i = 0; i < tabCount; i++)
+ {
+ buffer.Append(Tab);
+ }
+
+ buffer.Append("}\r\n");
+ }
+
+ private static void EmbedOne(StringBuilder buffer, string indent, int tabCount, in MemberInfoTuple member, bool canOverwrite)
+ {
+ const string Tab = " ";
+ var binary = member.Binary.AsSpan((tabCount - 1) << 3);
+
+ switch (binary.Length)
+ {
+ case 1:
+ buffer.Append("if (stringKey[0] != ").Append(binary[0]);
+ break;
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ buffer.Append("if (global::MessagePack.Internal.AutomataKeyGen.GetKey(ref stringKey) != ").Append(member.Key[tabCount - 1]).Append("UL");
+ break;
+ default:
+ EmbedSequenceEqual(buffer, member, (tabCount << 3) - 8);
+ break;
+ }
+
+ buffer.Append(") { goto FAIL; }\r\n\r\n").Append(indent);
+ for (var i = 0; i < tabCount; i++)
+ {
+ buffer.Append(Tab);
+ }
+
+ Assign(buffer, member, canOverwrite, indent, Tab, tabCount);
+ buffer.Append(indent);
+ for (var i = 0; i < tabCount; i++)
+ {
+ buffer.Append(Tab);
+ }
+
+ buffer.Append("continue;\r\n");
+ }
+
+ private static void EmbedSequenceEqual(StringBuilder buffer, MemberInfoTuple member, int startPosition)
+ {
+ buffer
+ .Append("if (!global::System.MemoryExtensions.SequenceEqual(stringKey, GetSpan_")
+ .Append(member.Info.Name)
+ .Append("().Slice(")
+ .Append(EmbedStringHelper.GetHeaderLength(member.Binary.Length));
+
+ if (startPosition != 0)
+ {
+ buffer.Append(" + ").Append(startPosition);
+ }
+
+ buffer.Append("))");
+ }
+}
+
+internal readonly struct MemberInfoTuple : IComparable
+{
+ public readonly MemberSerializationInfo Info;
+ public readonly bool IsConstructorParameter;
+ public readonly byte[] Binary;
+ public readonly ulong[] Key;
+
+ public MemberInfoTuple(MemberSerializationInfo info, bool isConstructorParameter)
+ {
+ Info = info;
+ IsConstructorParameter = isConstructorParameter;
+ Binary = EmbedStringHelper.Utf8.GetBytes(info.StringKey);
+ ReadOnlySpan span = Binary;
+ var keyLength = Binary.Length >> 3;
+ keyLength += keyLength << 3 == Binary.Length ? 0 : 1;
+ Key = new ulong[keyLength];
+ for (var i = 0; i < Key.Length; i++)
+ {
+ Key[i] = AutomataKeyGen.GetKey(ref span);
+ }
+ }
+
+ public int CompareTo(MemberInfoTuple other)
+ {
+ if (Info == other.Info)
+ {
+ return 0;
+ }
+
+ var c = Binary.Length.CompareTo(other.Binary.Length);
+ if (c != 0)
+ {
+ return c;
+ }
+
+ for (var i = 0; i < Key.Length; i++)
+ {
+ c = Key[i].CompareTo(other.Key[i]);
+ if (c != 0)
+ {
+ return c;
+ }
+ }
+
+ return 0;
+ }
+}
diff --git a/src/MessagePack.GeneratorCore/Generator/StringKey/StringKeyFormatterTemplate.cs b/src/MessagePack.Generator/Transforms/StringKey/StringKeyFormatterTemplate.cs
similarity index 99%
rename from src/MessagePack.GeneratorCore/Generator/StringKey/StringKeyFormatterTemplate.cs
rename to src/MessagePack.Generator/Transforms/StringKey/StringKeyFormatterTemplate.cs
index 083c69037..c987afa10 100644
--- a/src/MessagePack.GeneratorCore/Generator/StringKey/StringKeyFormatterTemplate.cs
+++ b/src/MessagePack.Generator/Transforms/StringKey/StringKeyFormatterTemplate.cs
@@ -7,12 +7,12 @@
// the code is regenerated.
//
// ------------------------------------------------------------------------------
-namespace MessagePackCompiler.Generator
+namespace MessagePack.Generator.Transforms
{
using System;
using System.Linq;
using System.Collections.Generic;
- using MessagePackCompiler.CodeAnalysis;
+ using MessagePack.Generator.CodeAnalysis;
///
/// Class to produce the template output
@@ -239,7 +239,7 @@ public class StringKeyFormatterTemplateBase
///
/// The string builder that generation-time code is using to assemble generated output
///
- protected System.Text.StringBuilder GenerationEnvironment
+ public System.Text.StringBuilder GenerationEnvironment
{
get
{
diff --git a/src/MessagePack.GeneratorCore/Generator/StringKey/StringKeyFormatterTemplate.tt b/src/MessagePack.Generator/Transforms/StringKey/StringKeyFormatterTemplate.tt
similarity index 99%
rename from src/MessagePack.GeneratorCore/Generator/StringKey/StringKeyFormatterTemplate.tt
rename to src/MessagePack.Generator/Transforms/StringKey/StringKeyFormatterTemplate.tt
index 6a6c3a8e2..81af93673 100644
--- a/src/MessagePack.GeneratorCore/Generator/StringKey/StringKeyFormatterTemplate.tt
+++ b/src/MessagePack.Generator/Transforms/StringKey/StringKeyFormatterTemplate.tt
@@ -3,7 +3,7 @@
<#@ import namespace="System" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Collections.Generic" #>
-<#@ import namespace="MessagePackCompiler.CodeAnalysis" #>
+<#@ import namespace="MessagePack.Generator.CodeAnalysis" #>
//
// THIS (.cs) FILE IS GENERATED BY MPC(MessagePack-CSharp). DO NOT CHANGE IT.
//
diff --git a/src/MessagePack.Generator/Transforms/TemplatePartials.cs b/src/MessagePack.Generator/Transforms/TemplatePartials.cs
new file mode 100644
index 000000000..a401eb7bd
--- /dev/null
+++ b/src/MessagePack.Generator/Transforms/TemplatePartials.cs
@@ -0,0 +1,79 @@
+// Copyright (c) All contributors. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+#pragma warning disable SA1402 // File may only contain a single type
+
+using MessagePack.Generator.CodeAnalysis;
+
+namespace MessagePack.Generator.Transforms;
+
+public partial class FormatterTemplate : IFormatterTemplate
+{
+ public FormatterTemplate(string @namespace, ObjectSerializationInfo[] objectSerializationInfos)
+ {
+ Namespace = @namespace;
+ ObjectSerializationInfos = objectSerializationInfos;
+ }
+
+ public string Namespace { get; }
+
+ public ObjectSerializationInfo[] ObjectSerializationInfos { get; }
+}
+
+public partial class StringKeyFormatterTemplate : IFormatterTemplate
+{
+ public StringKeyFormatterTemplate(string @namespace, ObjectSerializationInfo[] objectSerializationInfos)
+ {
+ Namespace = @namespace;
+ ObjectSerializationInfos = objectSerializationInfos;
+ }
+
+ public string Namespace { get; }
+
+ public ObjectSerializationInfo[] ObjectSerializationInfos { get; }
+}
+
+public partial class ResolverTemplate
+{
+ public ResolverTemplate(string @namespace, string formatterNamespace, string resolverName, IResolverRegisterInfo[] registerInfos)
+ {
+ Namespace = @namespace;
+ FormatterNamespace = formatterNamespace;
+ ResolverName = resolverName;
+ RegisterInfos = registerInfos;
+ }
+
+ public string Namespace { get; }
+
+ public string FormatterNamespace { get; }
+
+ public string ResolverName { get; }
+
+ public IResolverRegisterInfo[] RegisterInfos { get; }
+}
+
+public partial class EnumTemplate
+{
+ public EnumTemplate(string @namespace, EnumSerializationInfo[] enumSerializationInfos)
+ {
+ Namespace = @namespace;
+ EnumSerializationInfos = enumSerializationInfos;
+ }
+
+ public string Namespace { get; }
+
+ public EnumSerializationInfo[] EnumSerializationInfos { get; }
+}
+
+public partial class UnionTemplate
+{
+ public UnionTemplate(string @namespace, UnionSerializationInfo[] unionSerializationInfos)
+ {
+ Namespace = @namespace;
+ UnionSerializationInfos = unionSerializationInfos;
+ }
+
+ public string Namespace { get; }
+
+ public UnionSerializationInfo[] UnionSerializationInfos { get; }
+}
diff --git a/src/MessagePack.GeneratorCore/Generator/UnionTemplate.cs b/src/MessagePack.Generator/Transforms/UnionTemplate.cs
similarity index 98%
rename from src/MessagePack.GeneratorCore/Generator/UnionTemplate.cs
rename to src/MessagePack.Generator/Transforms/UnionTemplate.cs
index 12e2abb8f..027cfeb57 100644
--- a/src/MessagePack.GeneratorCore/Generator/UnionTemplate.cs
+++ b/src/MessagePack.Generator/Transforms/UnionTemplate.cs
@@ -7,7 +7,7 @@
// the code is regenerated.
//
// ------------------------------------------------------------------------------
-namespace MessagePackCompiler.Generator
+namespace MessagePack.Generator.Transforms
{
using System.Linq;
using System.Text;
@@ -26,8 +26,6 @@ public partial class UnionTemplate : UnionTemplateBase
public virtual string TransformText()
{
this.Write(@"//
-// THIS (.cs) FILE IS GENERATED BY MPC(MessagePack-CSharp). DO NOT CHANGE IT.
-//
#pragma warning disable 618
#pragma warning disable 612
@@ -170,7 +168,7 @@ public class UnionTemplateBase
///
/// The string builder that generation-time code is using to assemble generated output
///
- protected System.Text.StringBuilder GenerationEnvironment
+ public System.Text.StringBuilder GenerationEnvironment
{
get
{
diff --git a/src/MessagePack.GeneratorCore/Generator/UnionTemplate.tt b/src/MessagePack.Generator/Transforms/UnionTemplate.tt
similarity index 97%
rename from src/MessagePack.GeneratorCore/Generator/UnionTemplate.tt
rename to src/MessagePack.Generator/Transforms/UnionTemplate.tt
index a3ecb671d..5bebcc7dd 100644
--- a/src/MessagePack.GeneratorCore/Generator/UnionTemplate.tt
+++ b/src/MessagePack.Generator/Transforms/UnionTemplate.tt
@@ -4,8 +4,6 @@
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
//
-// THIS (.cs) FILE IS GENERATED BY MPC(MessagePack-CSharp). DO NOT CHANGE IT.
-//
#pragma warning disable 618
#pragma warning disable 612
diff --git a/src/MessagePack.Generator/Utils/RoslynExtensions.cs b/src/MessagePack.Generator/Utils/RoslynExtensions.cs
new file mode 100644
index 000000000..6a1649fc9
--- /dev/null
+++ b/src/MessagePack.Generator/Utils/RoslynExtensions.cs
@@ -0,0 +1,48 @@
+// Copyright (c) All contributors. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using Microsoft.CodeAnalysis;
+
+namespace MessagePack.Generator;
+
+// Utility and Extension methods for Roslyn
+internal static class RoslynExtensions
+{
+ public static IEnumerable GetNamedTypeSymbols(this Compilation compilation)
+ {
+ return compilation.SyntaxTrees.SelectMany(syntaxTree =>
+ {
+ var semModel = compilation.GetSemanticModel(syntaxTree);
+ return syntaxTree.GetRoot()
+ .DescendantNodes()
+ .Select(x => semModel.GetDeclaredSymbol(x))
+ .OfType();
+ });
+ }
+
+ public static IEnumerable GetAllMembers(this ITypeSymbol symbol)
+ {
+ var t = symbol;
+ while (t != null)
+ {
+ foreach (var item in t.GetMembers())
+ {
+ yield return item;
+ }
+
+ t = t.BaseType;
+ }
+ }
+
+ public static bool ApproximatelyEqual(this INamedTypeSymbol? left, INamedTypeSymbol? right)
+ {
+ if (left is IErrorTypeSymbol || right is IErrorTypeSymbol)
+ {
+ return left?.ToDisplayString() == right?.ToDisplayString();
+ }
+ else
+ {
+ return SymbolEqualityComparer.Default.Equals(left, right);
+ }
+ }
+}
diff --git a/src/MessagePack.GeneratorCore/.editorconfig b/src/MessagePack.GeneratorCore/.editorconfig
deleted file mode 100644
index 6b835fc65..000000000
--- a/src/MessagePack.GeneratorCore/.editorconfig
+++ /dev/null
@@ -1,4 +0,0 @@
-[*.cs]
-
-# VSTHRD111: Use ConfigureAwait(bool)
-dotnet_diagnostic.VSTHRD111.severity = none
diff --git a/src/MessagePack.GeneratorCore/CodeAnalysis/Definitions.cs b/src/MessagePack.GeneratorCore/CodeAnalysis/Definitions.cs
deleted file mode 100644
index c7282896d..000000000
--- a/src/MessagePack.GeneratorCore/CodeAnalysis/Definitions.cs
+++ /dev/null
@@ -1,282 +0,0 @@
-// Copyright (c) All contributors. All rights reserved.
-// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-
-using System;
-using System.Collections.Generic;
-using System.Linq;
-
-#pragma warning disable SA1402 // File may only contain a single type
-#pragma warning disable SA1649 // File name should match first type name
-
-namespace MessagePackCompiler.CodeAnalysis
-{
- public interface INamespaceInfo
- {
- string? Namespace { get; }
- }
-
- public interface IResolverRegisterInfo
- {
- string FullName { get; }
-
- string FormatterName { get; }
- }
-
- public class ObjectSerializationInfo : IResolverRegisterInfo, INamespaceInfo
- {
- public string Name { get; }
-
- public string FullName { get; }
-
- public string? Namespace { get; }
-
- public GenericTypeParameterInfo[] GenericTypeParameters { get; }
-
- public bool IsOpenGenericType { get; }
-
- public bool IsIntKey { get; }
-
- public bool IsStringKey
- {
- get { return !this.IsIntKey; }
- }
-
- public bool IsClass { get; }
-
- public MemberSerializationInfo[] ConstructorParameters { get; }
-
- public MemberSerializationInfo[] Members { get; }
-
- public bool HasIMessagePackSerializationCallbackReceiver { get; }
-
- public bool NeedsCastOnBefore { get; }
-
- public bool NeedsCastOnAfter { get; }
-
- public string FormatterName => this.Namespace == null ? FormatterNameWithoutNameSpace : this.Namespace + "." + FormatterNameWithoutNameSpace;
-
- public string FormatterNameWithoutNameSpace => this.Name + "Formatter" + (this.IsOpenGenericType ? $"<{string.Join(", ", this.GenericTypeParameters.Select(x => x.Name))}>" : string.Empty);
-
- public int WriteCount
- {
- get
- {
- if (this.IsStringKey)
- {
- return this.Members.Count(x => x.IsReadable);
- }
- else
- {
- return this.MaxKey;
- }
- }
- }
-
- public int MaxKey
- {
- get
- {
- return this.Members.Where(x => x.IsReadable).Select(x => x.IntKey).DefaultIfEmpty(-1).Max();
- }
- }
-
- public MemberSerializationInfo? GetMember(int index)
- {
- return this.Members.FirstOrDefault(x => x.IntKey == index);
- }
-
- public string GetConstructorString()
- {
- var args = string.Join(", ", this.ConstructorParameters.Select(x => "__" + x.Name + "__"));
- return $"{this.FullName}({args})";
- }
-
- public ObjectSerializationInfo(bool isClass, bool isOpenGenericType, GenericTypeParameterInfo[] genericTypeParameterInfos, MemberSerializationInfo[] constructorParameters, bool isIntKey, MemberSerializationInfo[] members, string name, string fullName, string? @namespace, bool hasSerializationConstructor, bool needsCastOnAfter, bool needsCastOnBefore)
- {
- IsClass = isClass;
- IsOpenGenericType = isOpenGenericType;
- GenericTypeParameters = genericTypeParameterInfos;
- ConstructorParameters = constructorParameters;
- IsIntKey = isIntKey;
- Members = members;
- Name = name;
- FullName = fullName;
- Namespace = @namespace;
- HasIMessagePackSerializationCallbackReceiver = hasSerializationConstructor;
- NeedsCastOnAfter = needsCastOnAfter;
- NeedsCastOnBefore = needsCastOnBefore;
- }
- }
-
- public class GenericTypeParameterInfo
- {
- public string Name { get; }
-
- public string Constraints { get; }
-
- public bool HasConstraints { get; }
-
- public GenericTypeParameterInfo(string name, string constraints)
- {
- Name = name ?? throw new ArgumentNullException(nameof(name));
- Constraints = constraints ?? throw new ArgumentNullException(nameof(name));
- HasConstraints = constraints != string.Empty;
- }
- }
-
- public class MemberSerializationInfo
- {
- public bool IsProperty { get; }
-
- public bool IsWritable { get; }
-
- public bool IsReadable { get; }
-
- public int IntKey { get; }
-
- public string StringKey { get; }
-
- public string Type { get; }
-
- public string Name { get; }
-
- public string ShortTypeName { get; }
-
- public string? CustomFormatterTypeName { get; }
-
- private readonly HashSet primitiveTypes = new(Generator.ShouldUseFormatterResolverHelper.PrimitiveTypes);
-
- public MemberSerializationInfo(bool isProperty, bool isWritable, bool isReadable, int intKey, string stringKey, string name, string type, string shortTypeName, string? customFormatterTypeName)
- {
- IsProperty = isProperty;
- IsWritable = isWritable;
- IsReadable = isReadable;
- IntKey = intKey;
- StringKey = stringKey;
- Type = type;
- Name = name;
- ShortTypeName = shortTypeName;
- CustomFormatterTypeName = customFormatterTypeName;
- }
-
- public string GetSerializeMethodString()
- {
- if (CustomFormatterTypeName != null)
- {
- return $"this.__{this.Name}CustomFormatter__.Serialize(ref writer, value.{this.Name}, options)";
- }
- else if (this.primitiveTypes.Contains(this.Type))
- {
- return "writer.Write(value." + this.Name + ")";
- }
- else
- {
- return $"global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify<{this.Type}>(formatterResolver).Serialize(ref writer, value.{this.Name}, options)";
- }
- }
-
- public string GetDeserializeMethodString()
- {
- if (CustomFormatterTypeName != null)
- {
- return $"this.__{this.Name}CustomFormatter__.Deserialize(ref reader, options)";
- }
- else if (this.primitiveTypes.Contains(this.Type))
- {
- if (this.Type == "byte[]")
- {
- return "global::MessagePack.Internal.CodeGenHelpers.GetArrayFromNullableSequence(reader.ReadBytes())";
- }
- else
- {
- return $"reader.Read{this.ShortTypeName!.Replace("[]", "s")}()";
- }
- }
- else
- {
- return $"global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify<{this.Type}>(formatterResolver).Deserialize(ref reader, options)";
- }
- }
- }
-
- public class EnumSerializationInfo : IResolverRegisterInfo, INamespaceInfo
- {
- public EnumSerializationInfo(string? @namespace, string name, string fullName, string underlyingType)
- {
- Namespace = @namespace;
- Name = name;
- FullName = fullName;
- UnderlyingType = underlyingType;
- }
-
- public string? Namespace { get; }
-
- public string Name { get; }
-
- public string FullName { get; }
-
- public string UnderlyingType { get; }
-
- public string FormatterName => (this.Namespace == null ? this.Name : this.Namespace + "." + this.Name) + "Formatter";
- }
-
- public class GenericSerializationInfo : IResolverRegisterInfo, IEquatable
- {
- public string FullName { get; }
-
- public string FormatterName { get; }
-
- public bool IsOpenGenericType { get; }
-
- public bool Equals(GenericSerializationInfo? other)
- {
- return this.FullName.Equals(other?.FullName);
- }
-
- public override int GetHashCode()
- {
- return this.FullName.GetHashCode();
- }
-
- public GenericSerializationInfo(string fullName, string formatterName, bool isOpenGenericType)
- {
- FullName = fullName;
- FormatterName = formatterName;
- IsOpenGenericType = isOpenGenericType;
- }
- }
-
- public class UnionSerializationInfo : IResolverRegisterInfo, INamespaceInfo
- {
- public string? Namespace { get; }
-
- public string Name { get; }
-
- public string FullName { get; }
-
- public string FormatterName => (this.Namespace == null ? this.Name : this.Namespace + "." + this.Name) + "Formatter";
-
- public UnionSubTypeInfo[] SubTypes { get; }
-
- public UnionSerializationInfo(string? @namespace, string name, string fullName, UnionSubTypeInfo[] subTypes)
- {
- Namespace = @namespace;
- Name = name;
- FullName = fullName;
- SubTypes = subTypes;
- }
- }
-
- public class UnionSubTypeInfo
- {
- public UnionSubTypeInfo(int key, string type)
- {
- Key = key;
- Type = type;
- }
-
- public int Key { get; }
-
- public string Type { get; }
- }
-}
diff --git a/src/MessagePack.GeneratorCore/CodeAnalysis/TypeCollector.cs b/src/MessagePack.GeneratorCore/CodeAnalysis/TypeCollector.cs
deleted file mode 100644
index 5558a67c4..000000000
--- a/src/MessagePack.GeneratorCore/CodeAnalysis/TypeCollector.cs
+++ /dev/null
@@ -1,1094 +0,0 @@
-// Copyright (c) All contributors. All rights reserved.
-// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-
-#pragma warning disable SA1402 // File may only contain a single type
-#pragma warning disable SA1649 // File name should match first type name
-
-using System;
-using System.Collections.Generic;
-using System.Collections.Immutable;
-using System.Linq;
-using System.Text;
-using System.Text.RegularExpressions;
-using Microsoft.CodeAnalysis;
-
-namespace MessagePackCompiler.CodeAnalysis
-{
- public class MessagePackGeneratorResolveFailedException : Exception
- {
- public MessagePackGeneratorResolveFailedException(string message)
- : base(message)
- {
- }
- }
-
- internal class ReferenceSymbols
- {
-#pragma warning disable SA1401 // Fields should be private
- internal readonly INamedTypeSymbol? Task;
- internal readonly INamedTypeSymbol? TaskOfT;
- internal readonly INamedTypeSymbol MessagePackObjectAttribute;
- internal readonly INamedTypeSymbol UnionAttribute;
- internal readonly INamedTypeSymbol SerializationConstructorAttribute;
- internal readonly INamedTypeSymbol KeyAttribute;
- internal readonly INamedTypeSymbol IgnoreAttribute;
- internal readonly INamedTypeSymbol? IgnoreDataMemberAttribute;
- internal readonly INamedTypeSymbol IMessagePackSerializationCallbackReceiver;
- internal readonly INamedTypeSymbol MessagePackFormatterAttribute;
-#pragma warning restore SA1401 // Fields should be private
-
- public ReferenceSymbols(Compilation compilation, Action logger)
- {
- TaskOfT = compilation.GetTypeByMetadataName("System.Threading.Tasks.Task`1");
- if (TaskOfT == null)
- {
- logger("failed to get metadata of System.Threading.Tasks.Task`1");
- }
-
- Task = compilation.GetTypeByMetadataName("System.Threading.Tasks.Task");
- if (Task == null)
- {
- logger("failed to get metadata of System.Threading.Tasks.Task");
- }
-
- MessagePackObjectAttribute = compilation.GetTypeByMetadataName("MessagePack.MessagePackObjectAttribute")
- ?? throw new InvalidOperationException("failed to get metadata of MessagePack.MessagePackObjectAttribute");
-
- UnionAttribute = compilation.GetTypeByMetadataName("MessagePack.UnionAttribute")
- ?? throw new InvalidOperationException("failed to get metadata of MessagePack.UnionAttribute");
-
- SerializationConstructorAttribute = compilation.GetTypeByMetadataName("MessagePack.SerializationConstructorAttribute")
- ?? throw new InvalidOperationException("failed to get metadata of MessagePack.SerializationConstructorAttribute");
-
- KeyAttribute = compilation.GetTypeByMetadataName("MessagePack.KeyAttribute")
- ?? throw new InvalidOperationException("failed to get metadata of MessagePack.KeyAttribute");
-
- IgnoreAttribute = compilation.GetTypeByMetadataName("MessagePack.IgnoreMemberAttribute")
- ?? throw new InvalidOperationException("failed to get metadata of MessagePack.IgnoreMemberAttribute");
-
- IgnoreDataMemberAttribute = compilation.GetTypeByMetadataName("System.Runtime.Serialization.IgnoreDataMemberAttribute");
- if (IgnoreDataMemberAttribute == null)
- {
- logger("failed to get metadata of System.Runtime.Serialization.IgnoreDataMemberAttribute");
- }
-
- IMessagePackSerializationCallbackReceiver = compilation.GetTypeByMetadataName("MessagePack.IMessagePackSerializationCallbackReceiver")
- ?? throw new InvalidOperationException("failed to get metadata of MessagePack.IMessagePackSerializationCallbackReceiver");
-
- MessagePackFormatterAttribute = compilation.GetTypeByMetadataName("MessagePack.MessagePackFormatterAttribute")
- ?? throw new InvalidOperationException("failed to get metadata of MessagePack.MessagePackFormatterAttribute");
- }
- }
-
- public class TypeCollector
- {
- private static readonly SymbolDisplayFormat BinaryWriteFormat = new SymbolDisplayFormat(
- genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters,
- miscellaneousOptions: SymbolDisplayMiscellaneousOptions.ExpandNullable,
- typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameOnly);
-
- private static readonly SymbolDisplayFormat ShortTypeNameFormat = new SymbolDisplayFormat(
- typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypes);
-
- private readonly bool isForceUseMap;
- private readonly ReferenceSymbols typeReferences;
- private readonly ITypeSymbol[] targetTypes;
- private readonly HashSet embeddedTypes = new(new[]
- {
- "short",
- "int",
- "long",
- "ushort",
- "uint",
- "ulong",
- "float",
- "double",
- "bool",
- "byte",
- "sbyte",
- "decimal",
- "char",
- "string",
- "object",
- "System.Guid",
- "System.TimeSpan",
- "System.DateTime",
- "System.DateTimeOffset",
-
- "MessagePack.Nil",
-
- // and arrays
- "short[]",
- "int[]",
- "long[]",
- "ushort[]",
- "uint[]",
- "ulong[]",
- "float[]",
- "double[]",
- "bool[]",
- "byte[]",
- "sbyte[]",
- "decimal[]",
- "char[]",
- "string[]",
- "System.DateTime[]",
- "System.ArraySegment",
- "System.ArraySegment?",
-
- // extensions
- "UnityEngine.Vector2",
- "UnityEngine.Vector3",
- "UnityEngine.Vector4",
- "UnityEngine.Quaternion",
- "UnityEngine.Color",
- "UnityEngine.Bounds",
- "UnityEngine.Rect",
- "UnityEngine.AnimationCurve",
- "UnityEngine.RectOffset",
- "UnityEngine.Gradient",
- "UnityEngine.WrapMode",
- "UnityEngine.GradientMode",
- "UnityEngine.Keyframe",
- "UnityEngine.Matrix4x4",
- "UnityEngine.GradientColorKey",
- "UnityEngine.GradientAlphaKey",
- "UnityEngine.Color32",
- "UnityEngine.LayerMask",
- "UnityEngine.Vector2Int",
- "UnityEngine.Vector3Int",
- "UnityEngine.RangeInt",
- "UnityEngine.RectInt",
- "UnityEngine.BoundsInt",
-
- "System.Reactive.Unit",
- });
-
- private readonly Dictionary knownGenericTypes = new()
- {
-#pragma warning disable SA1509 // Opening braces should not be preceded by blank line
- { "System.Collections.Generic.List<>", "global::MessagePack.Formatters.ListFormatter" },
- { "System.Collections.Generic.LinkedList<>", "global::MessagePack.Formatters.LinkedListFormatter" },
- { "System.Collections.Generic.Queue<>", "global::MessagePack.Formatters.QueueFormatter" },
- { "System.Collections.Generic.Stack<>", "global::MessagePack.Formatters.StackFormatter" },
- { "System.Collections.Generic.HashSet<>", "global::MessagePack.Formatters.HashSetFormatter" },
- { "System.Collections.ObjectModel.ReadOnlyCollection<>", "global::MessagePack.Formatters.ReadOnlyCollectionFormatter" },
- { "System.Collections.Generic.IList<>", "global::MessagePack.Formatters.InterfaceListFormatter2" },
- { "System.Collections.Generic.ICollection<>", "global::MessagePack.Formatters.InterfaceCollectionFormatter2" },
- { "System.Collections.Generic.IEnumerable<>", "global::MessagePack.Formatters.InterfaceEnumerableFormatter" },
- { "System.Collections.Generic.Dictionary<,>", "global::MessagePack.Formatters.DictionaryFormatter" },
- { "System.Collections.Generic.IDictionary<,>", "global::MessagePack.Formatters.InterfaceDictionaryFormatter" },
- { "System.Collections.Generic.SortedDictionary<,>", "global::MessagePack.Formatters.SortedDictionaryFormatter" },
- { "System.Collections.Generic.SortedList<,>", "global::MessagePack.Formatters.SortedListFormatter" },
- { "System.Linq.ILookup<,>", "global::MessagePack.Formatters.InterfaceLookupFormatter" },
- { "System.Linq.IGrouping<,>", "global::MessagePack.Formatters.InterfaceGroupingFormatter" },
- { "System.Collections.ObjectModel.ObservableCollection<>", "global::MessagePack.Formatters.ObservableCollectionFormatter" },
- { "System.Collections.ObjectModel.ReadOnlyObservableCollection<>", "global::MessagePack.Formatters.ReadOnlyObservableCollectionFormatter" },
- { "System.Collections.Generic.IReadOnlyList<>", "global::MessagePack.Formatters.InterfaceReadOnlyListFormatter" },
- { "System.Collections.Generic.IReadOnlyCollection<>", "global::MessagePack.Formatters.InterfaceReadOnlyCollectionFormatter" },
- { "System.Collections.Generic.ISet<>", "global::MessagePack.Formatters.InterfaceSetFormatter" },
- { "System.Collections.Concurrent.ConcurrentBag<>", "global::MessagePack.Formatters.ConcurrentBagFormatter" },
- { "System.Collections.Concurrent.ConcurrentQueue<>", "global::MessagePack.Formatters.ConcurrentQueueFormatter" },
- { "System.Collections.Concurrent.ConcurrentStack<>", "global::MessagePack.Formatters.ConcurrentStackFormatter" },
- { "System.Collections.ObjectModel.ReadOnlyDictionary<,>", "global::MessagePack.Formatters.ReadOnlyDictionaryFormatter" },
- { "System.Collections.Generic.IReadOnlyDictionary<,>", "global::MessagePack.Formatters.InterfaceReadOnlyDictionaryFormatter" },
- { "System.Collections.Concurrent.ConcurrentDictionary<,>", "global::MessagePack.Formatters.ConcurrentDictionaryFormatter" },
- { "System.Lazy<>", "global::MessagePack.Formatters.LazyFormatter" },
- { "System.Threading.Tasks<>", "global::MessagePack.Formatters.TaskValueFormatter" },
-
- { "System.Tuple<>", "global::MessagePack.Formatters.TupleFormatter" },
- { "System.Tuple<,>", "global::MessagePack.Formatters.TupleFormatter" },
- { "System.Tuple<,,>", "global::MessagePack.Formatters.TupleFormatter" },
- { "System.Tuple<,,,>", "global::MessagePack.Formatters.TupleFormatter" },
- { "System.Tuple<,,,,>", "global::MessagePack.Formatters.TupleFormatter" },
- { "System.Tuple<,,,,,>", "global::MessagePack.Formatters.TupleFormatter" },
- { "System.Tuple<,,,,,,>", "global::MessagePack.Formatters.TupleFormatter" },
- { "System.Tuple<,,,,,,,>", "global::MessagePack.Formatters.TupleFormatter" },
-
- { "System.ValueTuple<>", "global::MessagePack.Formatters.ValueTupleFormatter" },
- { "System.ValueTuple<,>", "global::MessagePack.Formatters.ValueTupleFormatter" },
- { "System.ValueTuple<,,>", "global::MessagePack.Formatters.ValueTupleFormatter" },
- { "System.ValueTuple<,,,>", "global::MessagePack.Formatters.ValueTupleFormatter" },
- { "System.ValueTuple<,,,,>", "global::MessagePack.Formatters.ValueTupleFormatter" },
- { "System.ValueTuple<,,,,,>", "global::MessagePack.Formatters.ValueTupleFormatter" },
- { "System.ValueTuple<,,,,,,>", "global::MessagePack.Formatters.ValueTupleFormatter" },
- { "System.ValueTuple<,,,,,,,>", "global::MessagePack.Formatters.ValueTupleFormatter" },
-
- { "System.Collections.Generic.KeyValuePair<,>", "global::MessagePack.Formatters.KeyValuePairFormatter" },
- { "System.Threading.Tasks.ValueTask<>", "global::MessagePack.Formatters.KeyValuePairFormatter" },
- { "System.ArraySegment<>", "global::MessagePack.Formatters.ArraySegmentFormatter" },
-
- // extensions
- { "System.Collections.Immutable.ImmutableArray<>", "global::MessagePack.ImmutableCollection.ImmutableArrayFormatter" },
- { "System.Collections.Immutable.ImmutableList<>", "global::MessagePack.ImmutableCollection.ImmutableListFormatter" },
- { "System.Collections.Immutable.ImmutableDictionary<,>", "global::MessagePack.ImmutableCollection.ImmutableDictionaryFormatter" },
- { "System.Collections.Immutable.ImmutableHashSet<>", "global::MessagePack.ImmutableCollection.ImmutableHashSetFormatter" },
- { "System.Collections.Immutable.ImmutableSortedDictionary<,>", "global::MessagePack.ImmutableCollection.ImmutableSortedDictionaryFormatter" },
- { "System.Collections.Immutable.ImmutableSortedSet<>", "global::MessagePack.ImmutableCollection.ImmutableSortedSetFormatter" },
- { "System.Collections.Immutable.ImmutableQueue<>", "global::MessagePack.ImmutableCollection.ImmutableQueueFormatter" },
- { "System.Collections.Immutable.ImmutableStack<>", "global::MessagePack.ImmutableCollection.ImmutableStackFormatter" },
- { "System.Collections.Immutable.IImmutableList<>", "global::MessagePack.ImmutableCollection.InterfaceImmutableListFormatter" },
- { "System.Collections.Immutable.IImmutableDictionary<,>", "global::MessagePack.ImmutableCollection.InterfaceImmutableDictionaryFormatter" },
- { "System.Collections.Immutable.IImmutableQueue<>", "global::MessagePack.ImmutableCollection.InterfaceImmutableQueueFormatter" },
- { "System.Collections.Immutable.IImmutableSet<>", "global::MessagePack.ImmutableCollection.InterfaceImmutableSetFormatter" },
- { "System.Collections.Immutable.IImmutableStack<>", "global::MessagePack.ImmutableCollection.InterfaceImmutableStackFormatter" },
-
- { "Reactive.Bindings.ReactiveProperty<>", "global::MessagePack.ReactivePropertyExtension.ReactivePropertyFormatter" },
- { "Reactive.Bindings.IReactiveProperty<>", "global::MessagePack.ReactivePropertyExtension.InterfaceReactivePropertyFormatter" },
- { "Reactive.Bindings.IReadOnlyReactiveProperty<>", "global::MessagePack.ReactivePropertyExtension.InterfaceReadOnlyReactivePropertyFormatter" },
- { "Reactive.Bindings.ReactiveCollection<>", "global::MessagePack.ReactivePropertyExtension.ReactiveCollectionFormatter" },
-#pragma warning restore SA1509 // Opening braces should not be preceded by blank line
- };
-
- private readonly bool disallowInternal;
-
- private readonly bool excludeArrayElement;
-
- private readonly HashSet externalIgnoreTypeNames;
-
- // visitor workspace:
-#pragma warning disable RS1024 // Compare symbols correctly (https://github.com/dotnet/roslyn-analyzers/issues/5246)
- private readonly HashSet alreadyCollected = new(SymbolEqualityComparer.Default);
-#pragma warning restore RS1024 // Compare symbols correctly
- private readonly List collectedObjectInfo = new();
- private readonly List collectedEnumInfo = new();
- private readonly List collectedGenericInfo = new();
- private readonly List collectedUnionInfo = new();
-
- private readonly Compilation compilation;
-
- public TypeCollector(Compilation compilation, bool disallowInternal, bool isForceUseMap, string[]? ignoreTypeNames, Action logger)
- {
- this.typeReferences = new ReferenceSymbols(compilation, logger);
- this.disallowInternal = disallowInternal;
- this.isForceUseMap = isForceUseMap;
- this.externalIgnoreTypeNames = new HashSet(ignoreTypeNames ?? Array.Empty());
- this.compilation = compilation;
-
- targetTypes = compilation.GetNamedTypeSymbols()
- .Where(x =>
- {
- if (x.DeclaredAccessibility == Accessibility.Public)
- {
- return true;
- }
-
- if (!disallowInternal)
- {
- return x.DeclaredAccessibility == Accessibility.Friend;
- }
-
- return false;
- })
- .Where(x =>
- ((x.TypeKind == TypeKind.Interface) && x.GetAttributes().Any(x2 => x2.AttributeClass.ApproximatelyEqual(typeReferences.UnionAttribute)))
- || ((x.TypeKind == TypeKind.Class && x.IsAbstract) && x.GetAttributes().Any(x2 => x2.AttributeClass.ApproximatelyEqual(typeReferences.UnionAttribute)))
- || ((x.TypeKind == TypeKind.Class) && x.GetAttributes().Any(x2 => x2.AttributeClass.ApproximatelyEqual(typeReferences.MessagePackObjectAttribute)))
- || ((x.TypeKind == TypeKind.Struct) && x.GetAttributes().Any(x2 => x2.AttributeClass.ApproximatelyEqual(typeReferences.MessagePackObjectAttribute))))
- .ToArray();
- }
-
- public TypeCollector(Compilation compilation, bool disallowInternal, bool isForceUseMap, string[]? ignoreTypeNames, ITypeSymbol targetType)
- {
- this.typeReferences = new ReferenceSymbols(compilation, _ => { });
- this.disallowInternal = disallowInternal;
- this.isForceUseMap = isForceUseMap;
- this.externalIgnoreTypeNames = new HashSet(ignoreTypeNames ?? Array.Empty());
- this.compilation = compilation;
- this.excludeArrayElement = true;
-
- targetTypes = new[] { targetType }
- .Where(x =>
- {
- if (x.DeclaredAccessibility == Accessibility.Public)
- {
- return true;
- }
-
- if (!disallowInternal)
- {
- return x.DeclaredAccessibility == Accessibility.Friend;
- }
-
- return false;
- })
- .Where(x =>
- ((x.TypeKind == TypeKind.Interface) && x.GetAttributes().Any(x2 => x2.AttributeClass.ApproximatelyEqual(typeReferences.UnionAttribute)))
- || ((x.TypeKind == TypeKind.Class && x.IsAbstract) && x.GetAttributes().Any(x2 => x2.AttributeClass.ApproximatelyEqual(typeReferences.UnionAttribute)))
- || ((x.TypeKind == TypeKind.Class) && x.GetAttributes().Any(x2 => x2.AttributeClass.ApproximatelyEqual(typeReferences.MessagePackObjectAttribute)))
- || ((x.TypeKind == TypeKind.Struct) && x.GetAttributes().Any(x2 => x2.AttributeClass.ApproximatelyEqual(typeReferences.MessagePackObjectAttribute))))
- .ToArray();
- }
-
- private void ResetWorkspace()
- {
- this.alreadyCollected.Clear();
- this.collectedObjectInfo.Clear();
- this.collectedEnumInfo.Clear();
- this.collectedGenericInfo.Clear();
- this.collectedUnionInfo.Clear();
- }
-
- // EntryPoint
- public (ObjectSerializationInfo[] ObjectInfo, EnumSerializationInfo[] EnumInfo, GenericSerializationInfo[] GenericInfo, UnionSerializationInfo[] UnionInfo) Collect()
- {
- this.ResetWorkspace();
-
- foreach (var item in this.targetTypes)
- {
- this.CollectCore(item);
- }
-
- return (
- this.collectedObjectInfo.OrderBy(x => x.FullName).ToArray(),
- this.collectedEnumInfo.OrderBy(x => x.FullName).ToArray(),
- this.collectedGenericInfo.Distinct().OrderBy(x => x.FullName).ToArray(),
- this.collectedUnionInfo.OrderBy(x => x.FullName).ToArray());
- }
-
- // Gate of recursive collect
- private void CollectCore(ITypeSymbol typeSymbol)
- {
- if (!this.alreadyCollected.Add(typeSymbol))
- {
- return;
- }
-
- var typeSymbolString = typeSymbol.WithNullableAnnotation(NullableAnnotation.NotAnnotated).ToString() ?? throw new InvalidOperationException();
- if (this.embeddedTypes.Contains(typeSymbolString))
- {
- return;
- }
-
- if (this.externalIgnoreTypeNames.Contains(typeSymbolString))
- {
- return;
- }
-
- if (typeSymbol is IArrayTypeSymbol arrayTypeSymbol)
- {
- this.CollectArray((IArrayTypeSymbol)ToTupleUnderlyingType(arrayTypeSymbol));
- return;
- }
-
- if (!this.IsAllowAccessibility(typeSymbol))
- {
- return;
- }
-
- if (!(typeSymbol is INamedTypeSymbol type))
- {
- return;
- }
-
- var customFormatterAttr = typeSymbol.GetAttributes().FirstOrDefault(x => x.AttributeClass.ApproximatelyEqual(this.typeReferences.MessagePackFormatterAttribute));
- if (customFormatterAttr != null)
- {
- return;
- }
-
- if (type.EnumUnderlyingType != null)
- {
- this.CollectEnum(type, type.EnumUnderlyingType);
- return;
- }
-
- if (type.IsGenericType)
- {
- this.CollectGeneric((INamedTypeSymbol)ToTupleUnderlyingType(type));
- return;
- }
-
- if (type.Locations[0].IsInMetadata)
- {
- return;
- }
-
- if (type.TypeKind == TypeKind.Interface || (type.TypeKind == TypeKind.Class && type.IsAbstract))
- {
- this.CollectUnion(type);
- return;
- }
-
- this.CollectObject(type);
- }
-
- private void CollectEnum(INamedTypeSymbol type, ISymbol enumUnderlyingType)
- {
- var info = new EnumSerializationInfo(type.ContainingNamespace.IsGlobalNamespace ? null : type.ContainingNamespace.ToDisplayString(), type.ToDisplayString(ShortTypeNameFormat).Replace(".", "_"), type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), enumUnderlyingType.ToDisplayString(BinaryWriteFormat));
- this.collectedEnumInfo.Add(info);
- }
-
- private void CollectUnion(INamedTypeSymbol type)
- {
- ImmutableArray[] unionAttrs = type.GetAttributes().Where(x => x.AttributeClass.ApproximatelyEqual(this.typeReferences.UnionAttribute)).Select(x => x.ConstructorArguments).ToArray();
- if (unionAttrs.Length == 0)
- {
- throw new MessagePackGeneratorResolveFailedException("Serialization Type must mark UnionAttribute." + " type: " + type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat));
- }
-
- // 0, Int 1, SubType
- UnionSubTypeInfo UnionSubTypeInfoSelector(ImmutableArray x)
- {
- if (!(x[0] is { Value: int key }) || !(x[1] is { Value: ITypeSymbol typeSymbol }))
- {
- throw new NotSupportedException("AOT code generation only supports UnionAttribute that uses a Type parameter, but the " + type.ToDisplayString(SymbolDisplayFormat.CSharpErrorMessageFormat) + " type uses an unsupported parameter.");
- }
-
- var typeName = typeSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
- return new UnionSubTypeInfo(key, typeName);
- }
-
- var info = new UnionSerializationInfo(type.ContainingNamespace.IsGlobalNamespace ? null : type.ContainingNamespace.ToDisplayString(), type.Name, type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), unionAttrs.Select(UnionSubTypeInfoSelector).OrderBy(x => x.Key).ToArray());
-
- this.collectedUnionInfo.Add(info);
- }
-
- private void CollectGenericUnion(INamedTypeSymbol type)
- {
- var unionAttrs = type.GetAttributes().Where(x => x.AttributeClass.ApproximatelyEqual(this.typeReferences.UnionAttribute)).Select(x => x.ConstructorArguments);
- using var enumerator = unionAttrs.GetEnumerator();
- if (!enumerator.MoveNext())
- {
- return;
- }
-
- do
- {
- var x = enumerator.Current;
- if (x[1] is { Value: INamedTypeSymbol unionType } && alreadyCollected.Contains(unionType) == false)
- {
- CollectCore(unionType);
- }
- }
- while (enumerator.MoveNext());
- }
-
- private void CollectArray(IArrayTypeSymbol array)
- {
- ITypeSymbol elemType = array.ElementType;
- if (!excludeArrayElement)
- {
- this.CollectCore(elemType);
- }
-
- var fullName = array.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
- var elementTypeDisplayName = elemType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
- string formatterName;
- if (array.IsSZArray)
- {
- formatterName = "global::MessagePack.Formatters.ArrayFormatter<" + elementTypeDisplayName + ">";
- }
- else
- {
- formatterName = array.Rank switch
- {
- 2 => "global::MessagePack.Formatters.TwoDimensionalArrayFormatter<" + elementTypeDisplayName + ">",
- 3 => "global::MessagePack.Formatters.ThreeDimensionalArrayFormatter<" + elementTypeDisplayName + ">",
- 4 => "global::MessagePack.Formatters.FourDimensionalArrayFormatter<" + elementTypeDisplayName + ">",
- _ => throw new InvalidOperationException("does not supports array dimension, " + fullName),
- };
- }
-
- var info = new GenericSerializationInfo(fullName, formatterName, elemType is ITypeParameterSymbol);
- this.collectedGenericInfo.Add(info);
- }
-
- private ITypeSymbol ToTupleUnderlyingType(ITypeSymbol typeSymbol)
- {
- if (typeSymbol is IArrayTypeSymbol array)
- {
- return compilation.CreateArrayTypeSymbol(ToTupleUnderlyingType(array.ElementType), array.Rank);
- }
-
- if (typeSymbol is not INamedTypeSymbol namedType || !namedType.IsGenericType)
- {
- return typeSymbol;
- }
-
- namedType = namedType.TupleUnderlyingType ?? namedType;
- var newTypeArguments = namedType.TypeArguments.Select(ToTupleUnderlyingType).ToArray();
- if (!namedType.TypeArguments.SequenceEqual(newTypeArguments))
- {
- return namedType.ConstructedFrom.Construct(newTypeArguments);
- }
-
- return namedType;
- }
-
- private void CollectGeneric(INamedTypeSymbol type)
- {
- INamedTypeSymbol genericType = type.ConstructUnboundGenericType();
- var genericTypeString = genericType.ToDisplayString();
- var fullName = type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
- var isOpenGenericType = IsOpenGenericTypeRecursively(type);
-
- // special case
- if (fullName == "global::System.ArraySegment" || fullName == "global::System.ArraySegment?")
- {
- return;
- }
-
- // nullable
- if (genericTypeString == "T?")
- {
- var firstTypeArgument = type.TypeArguments[0];
- this.CollectCore(firstTypeArgument);
-
- if (this.embeddedTypes.Contains(firstTypeArgument.ToString()!))
- {
- return;
- }
-
- var info = new GenericSerializationInfo(type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), "global::MessagePack.Formatters.NullableFormatter<" + firstTypeArgument.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) + ">", isOpenGenericType);
- this.collectedGenericInfo.Add(info);
- return;
- }
-
- // collection
- if (this.knownGenericTypes.TryGetValue(genericTypeString, out var formatter))
- {
- foreach (ITypeSymbol item in type.TypeArguments)
- {
- this.CollectCore(item);
- }
-
- var typeArgs = string.Join(", ", type.TypeArguments.Select(x => x.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)));
- var f = formatter.Replace("TREPLACE", typeArgs);
-
- var info = new GenericSerializationInfo(type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), f, isOpenGenericType);
-
- this.collectedGenericInfo.Add(info);
-
- if (genericTypeString != "System.Linq.ILookup<,>")
- {
- return;
- }
-
- formatter = this.knownGenericTypes["System.Linq.IGrouping<,>"];
- f = formatter.Replace("TREPLACE", typeArgs);
-
- var groupingInfo = new GenericSerializationInfo("global::System.Linq.IGrouping<" + typeArgs + ">", f, isOpenGenericType);
- this.collectedGenericInfo.Add(groupingInfo);
-
- formatter = this.knownGenericTypes["System.Collections.Generic.IEnumerable<>"];
- typeArgs = type.TypeArguments[1].ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
- f = formatter.Replace("TREPLACE", typeArgs);
-
- var enumerableInfo = new GenericSerializationInfo("global::System.Collections.Generic.IEnumerable<" + typeArgs + ">", f, isOpenGenericType);
- this.collectedGenericInfo.Add(enumerableInfo);
- return;
- }
-
- // Generic types
- if (type.IsDefinition)
- {
- this.CollectGenericUnion(type);
- this.CollectObject(type);
- return;
- }
- else
- {
- // Collect substituted types for the properties and fields.
- // NOTE: It is used to register formatters from nested generic type.
- // However, closed generic types such as `Foo` are not registered as a formatter.
- GetObjectInfo(type);
-
- // Collect generic type definition, that is not collected when it is defined outside target project.
- CollectCore(type.OriginalDefinition);
- }
-
- // Collect substituted types for the type parameters (e.g. Bar in Foo)
- foreach (var item in type.TypeArguments)
- {
- this.CollectCore(item);
- }
-
- var formatterBuilder = new StringBuilder();
- if (!type.ContainingNamespace.IsGlobalNamespace)
- {
- formatterBuilder.Append(type.ContainingNamespace.ToDisplayString() + ".");
- }
-
- formatterBuilder.Append(type.Name);
- formatterBuilder.Append("Formatter<");
- var typeArgumentIterator = type.TypeArguments.GetEnumerator();
- {
- if (typeArgumentIterator.MoveNext())
- {
- formatterBuilder.Append(typeArgumentIterator.Current.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat));
- }
-
- while (typeArgumentIterator.MoveNext())
- {
- formatterBuilder.Append(", ");
- formatterBuilder.Append(typeArgumentIterator.Current.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat));
- }
- }
-
- formatterBuilder.Append('>');
-
- var genericSerializationInfo = new GenericSerializationInfo(type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), formatterBuilder.ToString(), isOpenGenericType);
- this.collectedGenericInfo.Add(genericSerializationInfo);
- }
-
- private void CollectObject(INamedTypeSymbol type)
- {
- ObjectSerializationInfo info = GetObjectInfo(type);
- collectedObjectInfo.Add(info);
- }
-
- private ObjectSerializationInfo GetObjectInfo(INamedTypeSymbol type)
- {
- var isClass = !type.IsValueType;
- var isOpenGenericType = type.IsGenericType;
-
- AttributeData contractAttr = type.GetAttributes().FirstOrDefault(x => x.AttributeClass.ApproximatelyEqual(this.typeReferences.MessagePackObjectAttribute))
- ?? throw new MessagePackGeneratorResolveFailedException("Serialization Object must mark MessagePackObjectAttribute." + " type: " + type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat));
-
- var isIntKey = true;
- var intMembers = new Dictionary();
- var stringMembers = new Dictionary();
-
- if (this.isForceUseMap || (contractAttr.ConstructorArguments[0] is { Value: bool firstConstructorArgument } && firstConstructorArgument))
- {
- // All public members are serialize target except [Ignore] member.
- isIntKey = false;
-
- var hiddenIntKey = 0;
-
- foreach (IPropertySymbol item in type.GetAllMembers().OfType().Where(x => !x.IsOverride))
- {
- if (item.GetAttributes().Any(x => (x.AttributeClass.ApproximatelyEqual(this.typeReferences.IgnoreAttribute) || x.AttributeClass?.Name == this.typeReferences.IgnoreDataMemberAttribute?.Name)))
- {
- continue;
- }
-
- var isReadable = item.GetMethod != null && item.GetMethod.DeclaredAccessibility == Accessibility.Public && !item.IsStatic;
- var isWritable = item.SetMethod != null && item.SetMethod.DeclaredAccessibility == Accessibility.Public && !item.IsStatic;
- if (!isReadable && !isWritable)
- {
- continue;
- }
-
- var customFormatterAttr = item.GetAttributes().FirstOrDefault(x => x.AttributeClass.ApproximatelyEqual(this.typeReferences.MessagePackFormatterAttribute))?.ConstructorArguments[0].Value as INamedTypeSymbol;
- var member = new MemberSerializationInfo(true, isWritable, isReadable, hiddenIntKey++, item.Name, item.Name, item.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), item.Type.ToDisplayString(BinaryWriteFormat), customFormatterAttr?.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat));
- stringMembers.Add(member.StringKey, member);
-
- this.CollectCore(item.Type); // recursive collect
- }
-
- foreach (IFieldSymbol item in type.GetAllMembers().OfType())
- {
- if (item.GetAttributes().Any(x => (x.AttributeClass.ApproximatelyEqual(this.typeReferences.IgnoreAttribute) || x.AttributeClass?.Name == this.typeReferences.IgnoreDataMemberAttribute?.Name)))
- {
- continue;
- }
-
- if (item.IsImplicitlyDeclared)
- {
- continue;
- }
-
- var isReadable = item.DeclaredAccessibility == Accessibility.Public && !item.IsStatic;
- var isWritable = item.DeclaredAccessibility == Accessibility.Public && !item.IsReadOnly && !item.IsStatic;
- if (!isReadable && !isWritable)
- {
- continue;
- }
-
- var customFormatterAttr = item.GetAttributes().FirstOrDefault(x => x.AttributeClass.ApproximatelyEqual(this.typeReferences.MessagePackFormatterAttribute))?.ConstructorArguments[0].Value as INamedTypeSymbol;
- var member = new MemberSerializationInfo(false, isWritable, isReadable, hiddenIntKey++, item.Name, item.Name, item.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), item.Type.ToDisplayString(BinaryWriteFormat), customFormatterAttr?.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat));
- stringMembers.Add(member.StringKey, member);
- this.CollectCore(item.Type); // recursive collect
- }
- }
- else
- {
- // Only KeyAttribute members
- var searchFirst = true;
- var hiddenIntKey = 0;
-
- foreach (IPropertySymbol item in type.GetAllMembers().OfType())
- {
- if (item.IsIndexer)
- {
- continue; // .tt files don't generate good code for this yet: https://github.com/neuecc/MessagePack-CSharp/issues/390
- }
-
- if (item.GetAttributes().Any(x =>
- {
- var typeReferencesIgnoreDataMemberAttribute = this.typeReferences.IgnoreDataMemberAttribute;
- return typeReferencesIgnoreDataMemberAttribute != null && (x.AttributeClass.ApproximatelyEqual(this.typeReferences.IgnoreAttribute) || x.AttributeClass.ApproximatelyEqual(typeReferencesIgnoreDataMemberAttribute));
- }))
- {
- continue;
- }
-
- var isReadable = item.GetMethod != null && item.GetMethod.DeclaredAccessibility == Accessibility.Public && !item.IsStatic;
- var isWritable = item.SetMethod != null && item.SetMethod.DeclaredAccessibility == Accessibility.Public && !item.IsStatic;
- if (!isReadable && !isWritable)
- {
- continue;
- }
-
- var customFormatterAttr = item.GetAttributes().FirstOrDefault(x => x.AttributeClass.ApproximatelyEqual(this.typeReferences.MessagePackFormatterAttribute))?.ConstructorArguments[0].Value as INamedTypeSymbol;
- var key = item.GetAttributes().FirstOrDefault(x => x.AttributeClass.ApproximatelyEqual(this.typeReferences.KeyAttribute))?.ConstructorArguments[0]
- ?? throw new MessagePackGeneratorResolveFailedException("all public members must mark KeyAttribute or IgnoreMemberAttribute." + " type: " + type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) + " member:" + item.Name);
-
- var intKey = key is { Value: int intKeyValue } ? intKeyValue : default(int?);
- var stringKey = key is { Value: string stringKeyValue } ? stringKeyValue : default;
- if (intKey == null && stringKey == null)
- {
- throw new MessagePackGeneratorResolveFailedException("both IntKey and StringKey are null." + " type: " + type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) + " member:" + item.Name);
- }
-
- if (searchFirst)
- {
- searchFirst = false;
- isIntKey = intKey != null;
- }
- else
- {
- if ((isIntKey && intKey == null) || (!isIntKey && stringKey == null))
- {
- throw new MessagePackGeneratorResolveFailedException("all members key type must be same." + " type: " + type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) + " member:" + item.Name);
- }
- }
-
- if (isIntKey)
- {
- if (intMembers.ContainsKey(intKey!.Value))
- {
- throw new MessagePackGeneratorResolveFailedException("key is duplicated, all members key must be unique." + " type: " + type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) + " member:" + item.Name);
- }
-
- var member = new MemberSerializationInfo(true, isWritable, isReadable, intKey!.Value, item.Name, item.Name, item.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), item.Type.ToDisplayString(BinaryWriteFormat), customFormatterAttr?.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat));
- intMembers.Add(member.IntKey, member);
- }
- else
- {
- if (stringMembers.ContainsKey(stringKey!))
- {
- throw new MessagePackGeneratorResolveFailedException("key is duplicated, all members key must be unique." + " type: " + type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) + " member:" + item.Name);
- }
-
- var member = new MemberSerializationInfo(true, isWritable, isReadable, hiddenIntKey++, stringKey!, item.Name, item.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), item.Type.ToDisplayString(BinaryWriteFormat), customFormatterAttr?.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat));
- stringMembers.Add(member.StringKey, member);
- }
-
- var messagePackFormatter = item.GetAttributes().FirstOrDefault(x => x.AttributeClass.ApproximatelyEqual(this.typeReferences.MessagePackFormatterAttribute))?.ConstructorArguments[0];
-
- if (messagePackFormatter == null)
- {
- this.CollectCore(item.Type); // recursive collect
- }
- }
-
- foreach (IFieldSymbol item in type.GetAllMembers().OfType())
- {
- if (item.IsImplicitlyDeclared)
- {
- continue;
- }
-
- if (item.GetAttributes().Any(x => x.AttributeClass.ApproximatelyEqual(this.typeReferences.IgnoreAttribute)))
- {
- continue;
- }
-
- var isReadable = item.DeclaredAccessibility == Accessibility.Public && !item.IsStatic;
- var isWritable = item.DeclaredAccessibility == Accessibility.Public && !item.IsReadOnly && !item.IsStatic;
- if (!isReadable && !isWritable)
- {
- continue;
- }
-
- var customFormatterAttr = item.GetAttributes().FirstOrDefault(x => x.AttributeClass.ApproximatelyEqual(this.typeReferences.MessagePackFormatterAttribute))?.ConstructorArguments[0].Value as INamedTypeSymbol;
- var key = item.GetAttributes().FirstOrDefault(x => x.AttributeClass.ApproximatelyEqual(this.typeReferences.KeyAttribute))?.ConstructorArguments[0]
- ?? throw new MessagePackGeneratorResolveFailedException("all public members must mark KeyAttribute or IgnoreMemberAttribute." + " type: " + type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) + " member:" + item.Name);
-
- var intKey = key is { Value: int intKeyValue } ? intKeyValue : default(int?);
- var stringKey = key is { Value: string stringKeyValue } ? stringKeyValue : default;
- if (intKey == null && stringKey == null)
- {
- throw new MessagePackGeneratorResolveFailedException("both IntKey and StringKey are null." + " type: " + type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) + " member:" + item.Name);
- }
-
- if (searchFirst)
- {
- searchFirst = false;
- isIntKey = intKey != null;
- }
- else
- {
- if ((isIntKey && intKey == null) || (!isIntKey && stringKey == null))
- {
- throw new MessagePackGeneratorResolveFailedException("all members key type must be same." + " type: " + type.Name + " member:" + item.Name);
- }
- }
-
- if (isIntKey)
- {
- if (intMembers.ContainsKey(intKey!.Value))
- {
- throw new MessagePackGeneratorResolveFailedException("key is duplicated, all members key must be unique." + " type: " + type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) + " member:" + item.Name);
- }
-
- var member = new MemberSerializationInfo(true, isWritable, isReadable, intKey!.Value, item.Name, item.Name, item.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), item.Type.ToDisplayString(BinaryWriteFormat), customFormatterAttr?.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat));
- intMembers.Add(member.IntKey, member);
- }
- else
- {
- if (stringMembers.ContainsKey(stringKey!))
- {
- throw new MessagePackGeneratorResolveFailedException("key is duplicated, all members key must be unique." + " type: " + type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) + " member:" + item.Name);
- }
-
- var member = new MemberSerializationInfo(true, isWritable, isReadable, hiddenIntKey++, stringKey!, item.Name, item.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), item.Type.ToDisplayString(BinaryWriteFormat), customFormatterAttr?.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat));
- stringMembers.Add(member.StringKey, member);
- }
-
- this.CollectCore(item.Type); // recursive collect
- }
- }
-
- // GetConstructor
- var ctorEnumerator = default(IEnumerator);
- var ctor = type.Constructors.Where(x => x.DeclaredAccessibility == Accessibility.Public).SingleOrDefault(x => x.GetAttributes().Any(y => y.AttributeClass != null && y.AttributeClass.ApproximatelyEqual(this.typeReferences.SerializationConstructorAttribute)));
- if (ctor == null)
- {
- ctorEnumerator = type.Constructors.Where(x => x.DeclaredAccessibility == Accessibility.Public).OrderByDescending(x => x.Parameters.Length).GetEnumerator();
-
- if (ctorEnumerator.MoveNext())
- {
- ctor = ctorEnumerator.Current;
- }
- }
-
- // struct allows null ctor
- if (ctor == null && isClass)
- {
- throw new MessagePackGeneratorResolveFailedException("can't find public constructor. type:" + type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat));
- }
-
- var constructorParameters = new List();
- if (ctor != null)
- {
- var constructorLookupDictionary = stringMembers.ToLookup(x => x.Key, x => x, StringComparer.OrdinalIgnoreCase);
- do
- {
- constructorParameters.Clear();
- var ctorParamIndex = 0;
- foreach (IParameterSymbol item in ctor!.Parameters)
- {
- MemberSerializationInfo paramMember;
- if (isIntKey)
- {
- if (intMembers.TryGetValue(ctorParamIndex, out paramMember!))
- {
- if (item.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) == paramMember.Type && paramMember.IsReadable)
- {
- constructorParameters.Add(paramMember);
- }
- else
- {
- if (ctorEnumerator != null)
- {
- ctor = null;
- continue;
- }
- else
- {
- throw new MessagePackGeneratorResolveFailedException("can't find matched constructor parameter, parameterType mismatch. type:" + type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) + " parameterIndex:" + ctorParamIndex + " parameterType:" + item.Type.Name);
- }
- }
- }
- else
- {
- if (ctorEnumerator != null)
- {
- ctor = null;
- continue;
- }
- else
- {
- throw new MessagePackGeneratorResolveFailedException("can't find matched constructor parameter, index not found. type:" + type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) + " parameterIndex:" + ctorParamIndex);
- }
- }
- }
- else
- {
- IEnumerable> hasKey = constructorLookupDictionary[item.Name];
- using var enumerator = hasKey.GetEnumerator();
-
- // hasKey.Count() == 0
- if (!enumerator.MoveNext())
- {
- if (ctorEnumerator == null)
- {
- throw new MessagePackGeneratorResolveFailedException("can't find matched constructor parameter, index not found. type:" + type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) + " parameterName:" + item.Name);
- }
-
- ctor = null;
- continue;
- }
-
- var first = enumerator.Current.Value;
-
- // hasKey.Count() != 1
- if (enumerator.MoveNext())
- {
- if (ctorEnumerator == null)
- {
- throw new MessagePackGeneratorResolveFailedException("duplicate matched constructor parameter name:" + type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) + " parameterName:" + item.Name + " parameterType:" + item.Type.Name);
- }
-
- ctor = null;
- continue;
- }
-
- paramMember = first;
- if (item.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) == paramMember.Type && paramMember.IsReadable)
- {
- constructorParameters.Add(paramMember);
- }
- else
- {
- if (ctorEnumerator == null)
- {
- throw new MessagePackGeneratorResolveFailedException("can't find matched constructor parameter, parameterType mismatch. type:" + type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) + " parameterName:" + item.Name + " parameterType:" + item.Type.Name);
- }
-
- ctor = null;
- continue;
- }
- }
-
- ctorParamIndex++;
- }
- }
- while (TryGetNextConstructor(ctorEnumerator, ref ctor));
-
- if (ctor == null)
- {
- throw new MessagePackGeneratorResolveFailedException("can't find matched constructor. type:" + type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat));
- }
- }
-
- var hasSerializationConstructor = type.AllInterfaces.Any(x => x.ApproximatelyEqual(this.typeReferences.IMessagePackSerializationCallbackReceiver));
- var needsCastOnBefore = true;
- var needsCastOnAfter = true;
- if (hasSerializationConstructor)
- {
- needsCastOnBefore = !type.GetMembers("OnBeforeSerialize").Any();
- needsCastOnAfter = !type.GetMembers("OnAfterDeserialize").Any();
- }
-
- var info = new ObjectSerializationInfo(isClass, isOpenGenericType, isOpenGenericType ? type.TypeParameters.Select(ToGenericTypeParameterInfo).ToArray() : Array.Empty(), constructorParameters.ToArray(), isIntKey, isIntKey ? intMembers.Values.ToArray() : stringMembers.Values.ToArray(), isOpenGenericType ? GetGenericFormatterClassName(type) : GetMinimallyQualifiedClassName(type), type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), type.ContainingNamespace.IsGlobalNamespace ? null : type.ContainingNamespace.ToDisplayString(), hasSerializationConstructor, needsCastOnAfter, needsCastOnBefore);
-
- return info;
- }
-
- private static GenericTypeParameterInfo ToGenericTypeParameterInfo(ITypeParameterSymbol typeParameter)
- {
- var constraints = new List();
-
- // `notnull`, `unmanaged`, `class`, `struct` constraint must come before any constraints.
- if (typeParameter.HasNotNullConstraint)
- {
- constraints.Add("notnull");
- }
-
- if (typeParameter.HasReferenceTypeConstraint)
- {
- constraints.Add(typeParameter.ReferenceTypeConstraintNullableAnnotation == NullableAnnotation.Annotated ? "class?" : "class");
- }
-
- if (typeParameter.HasValueTypeConstraint)
- {
- constraints.Add(typeParameter.HasUnmanagedTypeConstraint ? "unmanaged" : "struct");
- }
-
- // constraint types (IDisposable, IEnumerable ...)
- foreach (var t in typeParameter.ConstraintTypes)
- {
- var constraintTypeFullName = t.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat.AddMiscellaneousOptions(SymbolDisplayMiscellaneousOptions.IncludeNullableReferenceTypeModifier));
- constraints.Add(constraintTypeFullName);
- }
-
- // `new()` constraint must be last in constraints.
- if (typeParameter.HasConstructorConstraint)
- {
- constraints.Add("new()");
- }
-
- return new GenericTypeParameterInfo(typeParameter.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), string.Join(", ", constraints));
- }
-
- private static string GetGenericFormatterClassName(INamedTypeSymbol type)
- {
- return type.Name;
- }
-
- private static string GetMinimallyQualifiedClassName(INamedTypeSymbol type)
- {
- var name = type.ContainingType is object ? GetMinimallyQualifiedClassName(type.ContainingType) + "_" : string.Empty;
- name += type.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat);
- name = name.Replace('.', '_');
- name = name.Replace('<', '_');
- name = name.Replace('>', '_');
- name = Regex.Replace(name, @"\[([,])*\]", match => $"Array{match.Length - 1}");
- name = name.Replace("?", string.Empty);
- return name;
- }
-
- private static bool TryGetNextConstructor(IEnumerator? ctorEnumerator, ref IMethodSymbol? ctor)
- {
- if (ctorEnumerator == null || ctor != null)
- {
- return false;
- }
-
- if (ctorEnumerator.MoveNext())
- {
- ctor = ctorEnumerator.Current;
- return true;
- }
- else
- {
- ctor = null;
- return false;
- }
- }
-
- private bool IsAllowAccessibility(ITypeSymbol symbol)
- {
- do
- {
- if (symbol.DeclaredAccessibility != Accessibility.Public)
- {
- if (this.disallowInternal)
- {
- return false;
- }
-
- if (symbol.DeclaredAccessibility != Accessibility.Internal)
- {
- return true;
- }
- }
-
- symbol = symbol.ContainingType;
- }
- while (symbol != null);
-
- return true;
- }
-
- private bool IsOpenGenericTypeRecursively(INamedTypeSymbol type)
- {
- return type.IsGenericType && type.TypeArguments.Any(x => x is ITypeParameterSymbol || (x is INamedTypeSymbol symbol && IsOpenGenericTypeRecursively(symbol)));
- }
- }
-}
diff --git a/src/MessagePack.GeneratorCore/CodeGenerator.cs b/src/MessagePack.GeneratorCore/CodeGenerator.cs
deleted file mode 100644
index 1f48973e3..000000000
--- a/src/MessagePack.GeneratorCore/CodeGenerator.cs
+++ /dev/null
@@ -1,319 +0,0 @@
-// Copyright (c) All contributors. All rights reserved.
-// 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;
-using System.Text;
-using System.Threading;
-using System.Threading.Tasks;
-using MessagePackCompiler.CodeAnalysis;
-using MessagePackCompiler.Generator;
-using Microsoft.CodeAnalysis;
-
-namespace MessagePackCompiler
-{
- public class CodeGenerator
- {
- private static readonly HashSet InvalidFileCharSet = new(Path.GetInvalidFileNameChars());
-
- private static readonly Encoding NoBomUtf8 = new UTF8Encoding(false);
-
- private readonly Action logger;
-
- public CodeGenerator(Action logger, CancellationToken cancellationToken)
- {
- this.logger = logger;
- }
-
- ///
- /// Generates the specialized resolver and formatters for the types that require serialization in a given compilation.
- ///
- /// The compilation to read types from as an input to code generation.
- /// The name of the generated source file.
- /// The resolver name.
- /// The namespace for the generated type to be created in. May be null.
- /// A boolean value that indicates whether all formatters should use property maps instead of more compact arrays.
- /// A comma-delimited list of symbols that should surround redundant generated files. May be null.
- /// May be null.
- /// A task that indicates when generation has completed.
- public async Task GenerateFileAsync(
- Compilation compilation,
- string output,
- string resolverName,
- string? @namespace,
- bool useMapMode,
- string? multipleIfDirectiveOutputSymbols,
- string[]? externalIgnoreTypeNames)
- {
- var namespaceDot = string.IsNullOrWhiteSpace(@namespace) ? string.Empty : @namespace + ".";
- var multipleOutputSymbols = multipleIfDirectiveOutputSymbols?.Split(',') ?? Array.Empty();
-
- var sw = Stopwatch.StartNew();
-
- 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, Console.WriteLine);
-
- logger("Project Compilation Complete:" + sw.Elapsed.ToString());
-
- sw.Restart();
- logger("Method Collect Start");
-
- var (objectInfo, enumInfo, genericInfo, unionInfo) = collector.Collect();
-
- logger("Method Collect Complete:" + sw.Elapsed.ToString());
-
- logger("Output Generation Start");
- sw.Restart();
-
- if (Path.GetExtension(output) == ".cs")
- {
- // SingleFile Output
- 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);
- }
-
- if (objectInfo.Length == 0 && enumInfo.Length == 0 && genericInfo.Length == 0 && unionInfo.Length == 0)
- {
- logger("Generated result is empty, unexpected result?");
- }
- }
-
- 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 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(IGrouping x)
- {
- if (x.Key == null)
- {
- return namespaceDot + "Formatters";
- }
-
- return namespaceDot + "Formatters." + x.Key;
- }
-
- var enumFormatterTemplates = enumInfo
- .GroupBy(x => x.Namespace)
- .Select(x => new EnumTemplate(GetNamespace(x), x.ToArray()))
- .ToArray();
-
- var unionFormatterTemplates = unionInfo
- .GroupBy(x => x.Namespace)
- .Select(x => new UnionTemplate(GetNamespace(x), x.ToArray()))
- .ToArray();
-
- var resolverTemplate = new ResolverTemplate(namespaceDot + "Resolvers", namespaceDot + "Formatters", resolverName, 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)
- {
- 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().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)
- {
- builder.Append(Path.DirectorySeparatorChar);
- }
- }
- }
-
- void AppendChar(char c)
- {
- if (c == '.' || InvalidFileCharSet.Contains(c))
- {
- builder.Append('_');
- }
- else
- {
- builder.Append(c);
- }
- }
-
- void Append(string text)
- {
- var span = text.AsSpan();
- while (!span.IsEmpty)
- {
- var index = span.IndexOf("global::".AsSpan());
- if (index == -1)
- {
- foreach (var c in span)
- {
- AppendChar(c);
- }
-
- break;
- }
-
- if (index == 0)
- {
- span = span.Slice("global::".Length);
- continue;
- }
-
- foreach (var c in span.Slice(0, index))
- {
- AppendChar(c);
- }
-
- span = span.Slice(index + "global::".Length);
- }
- }
-
- AppendDir(dir);
-
- if (!string.IsNullOrWhiteSpace(multipleOutSymbol))
- {
- text = $"#if {multipleOutSymbol}" + Environment.NewLine + text + Environment.NewLine + "#endif";
- AppendDir(MultiSymbolToSafeFilePath(multipleOutSymbol));
- }
-
- Append(ns);
- builder.Append('_');
- Append(name);
- builder.Append(".cs");
-
- return OutputAsync(builder.ToString(), text);
- }
-
- private Task OutputAsync(string path, string text)
- {
- path = path.Replace("global::", string.Empty);
-
- const string prefix = "[Out]";
- logger(prefix + path);
-
- var fi = new FileInfo(path);
- if (fi.Directory != null && !fi.Directory.Exists)
- {
- fi.Directory.Create();
- }
-
- File.WriteAllText(path, NormalizeNewLines(text), NoBomUtf8);
- return Task.CompletedTask;
- }
-
- private static string MultiSymbolToSafeFilePath(string symbol)
- {
- return symbol.Replace("!", "NOT_").Replace("(", string.Empty).Replace(")", string.Empty).Replace("||", "_OR_").Replace("&&", "_AND_");
- }
-
- private static string NormalizeNewLines(string content)
- {
- // The T4 generated code may be text with mixed line ending types. (CR + CRLF)
- // We need to normalize the line ending type in each Operating Systems. (e.g. Windows=CRLF, Linux/macOS=LF)
- return content.Replace("\r\n", "\n").Replace("\n", Environment.NewLine);
- }
- }
-}
diff --git a/src/MessagePack.GeneratorCore/Generator/IFormatterTemplate.cs b/src/MessagePack.GeneratorCore/Generator/IFormatterTemplate.cs
deleted file mode 100644
index ea3363a6e..000000000
--- a/src/MessagePack.GeneratorCore/Generator/IFormatterTemplate.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright (c) All contributors. All rights reserved.
-// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-
-using MessagePackCompiler.CodeAnalysis;
-
-namespace MessagePackCompiler.Generator
-{
- public interface IFormatterTemplate
- {
- string Namespace { get; }
-
- ObjectSerializationInfo[] ObjectSerializationInfos { get; }
-
- string TransformText();
- }
-}
diff --git a/src/MessagePack.GeneratorCore/Generator/ShouldUseFormatterResolverHelper.cs b/src/MessagePack.GeneratorCore/Generator/ShouldUseFormatterResolverHelper.cs
deleted file mode 100644
index ab5e61866..000000000
--- a/src/MessagePack.GeneratorCore/Generator/ShouldUseFormatterResolverHelper.cs
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright (c) All contributors. All rights reserved.
-// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-
-using System;
-using MessagePackCompiler.CodeAnalysis;
-
-namespace MessagePackCompiler.Generator
-{
- public static class ShouldUseFormatterResolverHelper
- {
- ///
- /// Keep this list in sync with DynamicObjectTypeBuilder.IsOptimizeTargetType.
- ///
- internal static readonly string[] PrimitiveTypes =
- {
- "short",
- "int",
- "long",
- "ushort",
- "uint",
- "ulong",
- "float",
- "double",
- "bool",
- "byte",
- "sbyte",
- "char",
- "byte[]",
-
- // Do not include types that resolvers are allowed to modify.
- ////"global::System.DateTime", // OldSpec has no support, so for that and perf reasons a .NET native DateTime resolver exists.
- ////"string", // https://github.com/Cysharp/MasterMemory provides custom formatter for string interning.
- };
-
- public static bool ShouldUseFormatterResolver(MemberSerializationInfo[] infos)
- {
- foreach (var memberSerializationInfo in infos)
- {
- if (memberSerializationInfo.CustomFormatterTypeName == null && Array.IndexOf(PrimitiveTypes, memberSerializationInfo.Type) == -1)
- {
- return true;
- }
- }
-
- return false;
- }
- }
-}
diff --git a/src/MessagePack.GeneratorCore/Generator/StringKey/EmbedStringHelper.cs b/src/MessagePack.GeneratorCore/Generator/StringKey/EmbedStringHelper.cs
deleted file mode 100644
index f10e26087..000000000
--- a/src/MessagePack.GeneratorCore/Generator/StringKey/EmbedStringHelper.cs
+++ /dev/null
@@ -1,87 +0,0 @@
-// Copyright (c) All contributors. All rights reserved.
-// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-
-using System;
-using System.Text;
-
-namespace MessagePackCompiler.Generator
-{
- public static class EmbedStringHelper
- {
- public static readonly Encoding Utf8 = new UTF8Encoding(false);
-
- public static string ToByteArrayString(byte[] binary)
- {
- var headerLength = GetHeaderLength(binary.Length);
- Span header = stackalloc byte[headerLength];
- EmbedHeader(binary.Length, header);
- var buffer = new StringBuilder().Append("new byte[").Append(headerLength).Append(" + ").Append(binary.Length).Append("] { ").Append(header[0]);
- foreach (var b in header.Slice(1))
- {
- buffer.Append(", ").Append(b);
- }
-
- foreach (var b in binary)
- {
- buffer.Append(", ").Append(b);
- }
-
- return buffer.Append(" }").ToString();
- }
-
- public static int GetHeaderLength(int byteCount)
- {
- if (byteCount <= 31)
- {
- return 1;
- }
-
- if (byteCount <= byte.MaxValue)
- {
- return 2;
- }
-
- return byteCount <= ushort.MaxValue ? 3 : 5;
- }
-
- public static void EmbedHeader(int byteCount, Span destination)
- {
- if (byteCount <= 31)
- {
- destination[0] = (byte)(0xa0 | byteCount);
- return;
- }
-
- if (byteCount <= byte.MaxValue)
- {
- destination[0] = 0xd9;
- destination[1] = unchecked((byte)byteCount);
- return;
- }
-
- if (byteCount <= ushort.MaxValue)
- {
- destination[0] = 0xda;
- destination[1] = unchecked((byte)(byteCount >> 8));
- destination[2] = unchecked((byte)byteCount);
- return;
- }
-
- destination[0] = 0xdb;
- destination[1] = unchecked((byte)(byteCount >> 24));
- destination[2] = unchecked((byte)(byteCount >> 16));
- destination[3] = unchecked((byte)(byteCount >> 8));
- destination[4] = unchecked((byte)byteCount);
- }
-
- public static byte[] GetEncodedStringBytes(string value)
- {
- var byteCount = Utf8.GetByteCount(value);
- var headerLength = GetHeaderLength(byteCount);
- var bytes = new byte[headerLength + byteCount];
- EmbedHeader(byteCount, bytes);
- Utf8.GetBytes(value, 0, value.Length, bytes, headerLength);
- return bytes;
- }
- }
-}
diff --git a/src/MessagePack.GeneratorCore/Generator/StringKey/StringKeyFormatterDeserializeHelper.cs b/src/MessagePack.GeneratorCore/Generator/StringKey/StringKeyFormatterDeserializeHelper.cs
deleted file mode 100644
index f65bed0b3..000000000
--- a/src/MessagePack.GeneratorCore/Generator/StringKey/StringKeyFormatterDeserializeHelper.cs
+++ /dev/null
@@ -1,252 +0,0 @@
-// Copyright (c) All contributors. All rights reserved.
-// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using MessagePack.Internal;
-using MessagePackCompiler.CodeAnalysis;
-
-namespace MessagePackCompiler.Generator
-{
- internal static class StringKeyFormatterDeserializeHelper
- {
- public static string Classify(ObjectSerializationInfo objectSerializationInfo, string indent, bool canOverwrite)
- {
- var memberArray = objectSerializationInfo.Members;
- var buffer = new StringBuilder();
- foreach (var memberInfoTuples in memberArray.Select(member => new MemberInfoTuple(member, IsConstructorParameter(objectSerializationInfo, member))).GroupBy(member => member.Binary.Length))
- {
- var binaryLength = memberInfoTuples.Key;
- var keyLength = binaryLength >> 3;
- keyLength += keyLength << 3 == binaryLength ? 0 : 1;
-
- buffer.Append(indent).Append("case ").Append(binaryLength).Append(":\r\n");
- ClassifyRecursion(buffer, indent, 1, keyLength, memberInfoTuples, canOverwrite);
- }
-
- return buffer.ToString();
- }
-
- private static bool IsConstructorParameter(ObjectSerializationInfo objectSerializationInfo, MemberSerializationInfo member)
- {
- foreach (var parameter in objectSerializationInfo.ConstructorParameters)
- {
- if (parameter.Equals(member))
- {
- return true;
- }
- }
-
- return false;
- }
-
- private static void Assign(StringBuilder buffer, in MemberInfoTuple member, bool canOverwrite, string indent, string tab, int tabCount)
- {
- if (member.Info.IsWritable || member.IsConstructorParameter)
- {
- if (canOverwrite)
- {
- buffer.Append("____result.").Append(member.Info.Name).Append(" = ");
- }
- else
- {
- if (!member.IsConstructorParameter)
- {
- buffer.Append("__").Append(member.Info.Name).Append("__IsInitialized = true;\r\n").Append(indent);
- for (var i = 0; i < tabCount; i++)
- {
- buffer.Append(tab);
- }
- }
-
- buffer.Append("__").Append(member.Info.Name).Append("__ = ");
- }
-
- buffer.Append(member.Info.GetDeserializeMethodString()).Append(";\r\n");
- }
- else
- {
- buffer.Append("reader.Skip();\r\n");
- }
- }
-
- private static void ClassifyRecursion(StringBuilder buffer, string indent, int tabCount, int keyLength, IEnumerable memberCollection, bool canOverwrite)
- {
- const string Tab = " ";
- buffer.Append(indent);
- for (var i = 0; i < tabCount; i++)
- {
- buffer.Append(Tab);
- }
-
- var memberArray = memberCollection.ToArray();
- if (memberArray.Length == 1)
- {
- var member = memberArray[0];
- EmbedOne(buffer, indent, tabCount, member, canOverwrite);
- return;
- }
-
- buffer.Append("switch (global::MessagePack.Internal.AutomataKeyGen.GetKey(ref stringKey))\r\n").Append(indent);
- for (var i = 0; i < tabCount; i++)
- {
- buffer.Append(Tab);
- }
-
- buffer.Append("{\r\n" + Tab).Append(indent);
- for (var i = 0; i < tabCount; i++)
- {
- buffer.Append(Tab);
- }
-
- buffer.Append("default: goto FAIL;");
-
- foreach (var grouping in memberArray.GroupBy(member => member.Key[tabCount - 1]))
- {
- buffer.Append("\r\n" + Tab).Append(indent);
- for (var i = 0; i < tabCount; i++)
- {
- buffer.Append(Tab);
- }
-
- buffer.Append("case ").Append(grouping.Key).Append("UL:\r\n");
-
- if (tabCount == keyLength)
- {
- buffer.Append(Tab + Tab).Append(indent);
- for (var i = 0; i < tabCount; i++)
- {
- buffer.Append(Tab);
- }
-
- var member = grouping.Single();
- Assign(buffer, member, canOverwrite, indent, Tab, tabCount + 2);
- buffer.Append(Tab + Tab).Append(indent);
- for (var i = 0; i < tabCount; i++)
- {
- buffer.Append(Tab);
- }
-
- buffer.Append("continue;");
- continue;
- }
-
- ClassifyRecursion(buffer, indent + Tab, tabCount + 1, keyLength, grouping, canOverwrite);
- }
-
- buffer.Append("\r\n").Append(indent);
- for (var i = 0; i < tabCount; i++)
- {
- buffer.Append(Tab);
- }
-
- buffer.Append("}\r\n");
- }
-
- private static void EmbedOne(StringBuilder buffer, string indent, int tabCount, in MemberInfoTuple member, bool canOverwrite)
- {
- const string Tab = " ";
- var binary = member.Binary.AsSpan((tabCount - 1) << 3);
-
- switch (binary.Length)
- {
- case 1:
- buffer.Append("if (stringKey[0] != ").Append(binary[0]);
- break;
- case 2:
- case 3:
- case 4:
- case 5:
- case 6:
- case 7:
- case 8:
- buffer.Append("if (global::MessagePack.Internal.AutomataKeyGen.GetKey(ref stringKey) != ").Append(member.Key[tabCount - 1]).Append("UL");
- break;
- default:
- EmbedSequenceEqual(buffer, member, (tabCount << 3) - 8);
- break;
- }
-
- buffer.Append(") { goto FAIL; }\r\n\r\n").Append(indent);
- for (var i = 0; i < tabCount; i++)
- {
- buffer.Append(Tab);
- }
-
- Assign(buffer, member, canOverwrite, indent, Tab, tabCount);
- buffer.Append(indent);
- for (var i = 0; i < tabCount; i++)
- {
- buffer.Append(Tab);
- }
-
- buffer.Append("continue;\r\n");
- }
-
- private static void EmbedSequenceEqual(StringBuilder buffer, MemberInfoTuple member, int startPosition)
- {
- buffer
- .Append("if (!global::System.MemoryExtensions.SequenceEqual(stringKey, GetSpan_")
- .Append(member.Info.Name)
- .Append("().Slice(")
- .Append(EmbedStringHelper.GetHeaderLength(member.Binary.Length));
-
- if (startPosition != 0)
- {
- buffer.Append(" + ").Append(startPosition);
- }
-
- buffer.Append("))");
- }
- }
-
- internal readonly struct MemberInfoTuple : IComparable
- {
- public readonly MemberSerializationInfo Info;
- public readonly bool IsConstructorParameter;
- public readonly byte[] Binary;
- public readonly ulong[] Key;
-
- public MemberInfoTuple(MemberSerializationInfo info, bool isConstructorParameter)
- {
- Info = info;
- IsConstructorParameter = isConstructorParameter;
- Binary = EmbedStringHelper.Utf8.GetBytes(info.StringKey);
- ReadOnlySpan span = Binary;
- var keyLength = Binary.Length >> 3;
- keyLength += keyLength << 3 == Binary.Length ? 0 : 1;
- Key = new ulong[keyLength];
- for (var i = 0; i < Key.Length; i++)
- {
- Key[i] = AutomataKeyGen.GetKey(ref span);
- }
- }
-
- public int CompareTo(MemberInfoTuple other)
- {
- if (Info == other.Info)
- {
- return 0;
- }
-
- var c = Binary.Length.CompareTo(other.Binary.Length);
- if (c != 0)
- {
- return c;
- }
-
- for (var i = 0; i < Key.Length; i++)
- {
- c = Key[i].CompareTo(other.Key[i]);
- if (c != 0)
- {
- return c;
- }
- }
-
- return 0;
- }
- }
-}
diff --git a/src/MessagePack.GeneratorCore/Generator/TemplatePartials.cs b/src/MessagePack.GeneratorCore/Generator/TemplatePartials.cs
deleted file mode 100644
index 9e04131b9..000000000
--- a/src/MessagePack.GeneratorCore/Generator/TemplatePartials.cs
+++ /dev/null
@@ -1,80 +0,0 @@
-// Copyright (c) All contributors. All rights reserved.
-// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-
-#pragma warning disable SA1402 // File may only contain a single type
-
-using MessagePackCompiler.CodeAnalysis;
-
-namespace MessagePackCompiler.Generator
-{
- public partial class FormatterTemplate : IFormatterTemplate
- {
- public FormatterTemplate(string @namespace, ObjectSerializationInfo[] objectSerializationInfos)
- {
- Namespace = @namespace;
- ObjectSerializationInfos = objectSerializationInfos;
- }
-
- public string Namespace { get; }
-
- public ObjectSerializationInfo[] ObjectSerializationInfos { get; }
- }
-
- public partial class StringKeyFormatterTemplate : IFormatterTemplate
- {
- public StringKeyFormatterTemplate(string @namespace, ObjectSerializationInfo[] objectSerializationInfos)
- {
- Namespace = @namespace;
- ObjectSerializationInfos = objectSerializationInfos;
- }
-
- public string Namespace { get; }
-
- public ObjectSerializationInfo[] ObjectSerializationInfos { get; }
- }
-
- public partial class ResolverTemplate
- {
- public ResolverTemplate(string @namespace, string formatterNamespace, string resolverName, IResolverRegisterInfo[] registerInfos)
- {
- Namespace = @namespace;
- FormatterNamespace = formatterNamespace;
- ResolverName = resolverName;
- RegisterInfos = registerInfos;
- }
-
- public string Namespace { get; }
-
- public string FormatterNamespace { get; }
-
- public string ResolverName { get; }
-
- public IResolverRegisterInfo[] RegisterInfos { get; }
- }
-
- public partial class EnumTemplate
- {
- public EnumTemplate(string @namespace, EnumSerializationInfo[] enumSerializationInfos)
- {
- Namespace = @namespace;
- EnumSerializationInfos = enumSerializationInfos;
- }
-
- public string Namespace { get; }
-
- public EnumSerializationInfo[] EnumSerializationInfos { get; }
- }
-
- public partial class UnionTemplate
- {
- public UnionTemplate(string @namespace, UnionSerializationInfo[] unionSerializationInfos)
- {
- Namespace = @namespace;
- UnionSerializationInfos = unionSerializationInfos;
- }
-
- public string Namespace { get; }
-
- public UnionSerializationInfo[] UnionSerializationInfos { get; }
- }
-}
diff --git a/src/MessagePack.GeneratorCore/MessagePack.GeneratorCore.csproj b/src/MessagePack.GeneratorCore/MessagePack.GeneratorCore.csproj
deleted file mode 100644
index 2714c70f3..000000000
--- a/src/MessagePack.GeneratorCore/MessagePack.GeneratorCore.csproj
+++ /dev/null
@@ -1,79 +0,0 @@
-
-
-
- netstandard2.0
- MessagePackCompiler
- false
-
-
-
-
-
-
-
-
-
- True
- True
- EnumTemplate.tt
-
-
- True
- True
- FormatterTemplate.tt
-
-
- True
- True
- ResolverTemplate.tt
-
-
- True
- True
- StringKeyFormatterTemplate.tt
-
-
- %(FileName).tt
- True
- True
-
-
- True
- True
- UnionTemplate.tt
-
-
-
-
-
- EnumTemplate.cs
- TextTemplatingFilePreprocessor
-
-
- FormatterTemplate.cs
- TextTemplatingFilePreprocessor
-
-
- ResolverTemplate.cs
- TextTemplatingFilePreprocessor
-
-
- StringKeyFormatterTemplate.cs
- TextTemplatingFilePreprocessor
- MessagePackCompiler.Generator
-
-
- UnionTemplate.cs
- TextTemplatingFilePreprocessor
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/MessagePack.GeneratorCore/Utils/RoslynExtensions.cs b/src/MessagePack.GeneratorCore/Utils/RoslynExtensions.cs
deleted file mode 100644
index b8645e11b..000000000
--- a/src/MessagePack.GeneratorCore/Utils/RoslynExtensions.cs
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright (c) All contributors. All rights reserved.
-// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-
-using System.Collections.Generic;
-using System.Linq;
-using Microsoft.CodeAnalysis;
-
-namespace MessagePackCompiler
-{
- // Utility and Extension methods for Roslyn
- internal static class RoslynExtensions
- {
- public static IEnumerable GetNamedTypeSymbols(this Compilation compilation)
- {
- return compilation.SyntaxTrees.SelectMany(syntaxTree =>
- {
- var semModel = compilation.GetSemanticModel(syntaxTree);
- return syntaxTree.GetRoot()
- .DescendantNodes()
- .Select(x => semModel.GetDeclaredSymbol(x))
- .OfType();
- });
- }
-
- public static IEnumerable GetAllMembers(this ITypeSymbol symbol)
- {
- var t = symbol;
- while (t != null)
- {
- foreach (var item in t.GetMembers())
- {
- yield return item;
- }
-
- t = t.BaseType;
- }
- }
-
- public static bool ApproximatelyEqual(this INamedTypeSymbol? left, INamedTypeSymbol? right)
- {
- if (left is IErrorTypeSymbol || right is IErrorTypeSymbol)
- {
- return left?.ToDisplayString() == right?.ToDisplayString();
- }
- else
- {
- return SymbolEqualityComparer.Default.Equals(left, right);
- }
- }
- }
-}
diff --git a/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Internal/AutomataDictionary.cs b/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Internal/AutomataDictionary.cs
index 0b6a21bd7..808dc10aa 100644
--- a/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Internal/AutomataDictionary.cs
+++ b/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Internal/AutomataDictionary.cs
@@ -408,95 +408,4 @@ private static void EmitSearchNextCore(ILGenerator il, LocalBuilder bytesSpan, L
#endif
}
}
-
- ///
- /// This is used by dynamically generated code. It can be made internal after we enable our dynamic assemblies to access internals.
- /// But that trick may require net46, so maybe we should leave this as public.
- ///
- public static class AutomataKeyGen
- {
- public static readonly MethodInfo GetKeyMethod = typeof(AutomataKeyGen).GetRuntimeMethod(nameof(GetKey), new[] { typeof(ReadOnlySpan).MakeByRefType() }) ?? throw new Exception("Unable to find our own APIs.");
-
- public static ulong GetKey(ref ReadOnlySpan span)
- {
- ulong key;
-
- unchecked
- {
- if (span.Length >= 8)
- {
- key = SafeBitConverter.ToUInt64(span);
- span = span.Slice(8);
- }
- else
- {
- switch (span.Length)
- {
- case 1:
- {
- key = span[0];
- span = span.Slice(1);
- break;
- }
-
- case 2:
- {
- key = SafeBitConverter.ToUInt16(span);
- span = span.Slice(2);
- break;
- }
-
- case 3:
- {
- var a = span[0];
- var b = SafeBitConverter.ToUInt16(span.Slice(1));
- key = a | (ulong)b << 8;
- span = span.Slice(3);
- break;
- }
-
- case 4:
- {
- key = SafeBitConverter.ToUInt32(span);
- span = span.Slice(4);
- break;
- }
-
- case 5:
- {
- var a = span[0];
- var b = SafeBitConverter.ToUInt32(span.Slice(1));
- key = a | (ulong)b << 8;
- span = span.Slice(5);
- break;
- }
-
- case 6:
- {
- ulong a = SafeBitConverter.ToUInt16(span);
- ulong b = SafeBitConverter.ToUInt32(span.Slice(2));
- key = a | (b << 16);
- span = span.Slice(6);
- break;
- }
-
- case 7:
- {
- var a = span[0];
- var b = SafeBitConverter.ToUInt16(span.Slice(1));
- var c = SafeBitConverter.ToUInt32(span.Slice(3));
- key = a | (ulong)b << 8 | (ulong)c << 24;
- span = span.Slice(7);
- break;
- }
-
- default:
- throw new MessagePackSerializationException("Not Supported Length");
- }
- }
-
- return key;
- }
- }
- }
}
diff --git a/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Internal/AutomataKeyGen.cs b/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Internal/AutomataKeyGen.cs
new file mode 100644
index 000000000..283fc9c83
--- /dev/null
+++ b/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Internal/AutomataKeyGen.cs
@@ -0,0 +1,109 @@
+// Copyright (c) All contributors. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.Buffers;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Reflection.Emit;
+using System.Runtime.InteropServices;
+using System.Text;
+
+#pragma warning disable SA1402 // File may only contain a single type
+#pragma warning disable SA1509 // Opening braces should not be preceded by blank line
+
+namespace MessagePack.Internal
+{
+ ///
+ /// This is used by dynamically generated code. It can be made internal after we enable our dynamic assemblies to access internals.
+ /// But that trick may require net46, so maybe we should leave this as public.
+ ///
+ public static class AutomataKeyGen
+ {
+ public static readonly MethodInfo GetKeyMethod = typeof(AutomataKeyGen).GetRuntimeMethod(nameof(GetKey), new[] { typeof(ReadOnlySpan).MakeByRefType() }) ?? throw new Exception("Unable to find our own APIs.");
+
+ public static ulong GetKey(ref ReadOnlySpan span)
+ {
+ ulong key;
+
+ unchecked
+ {
+ if (span.Length >= 8)
+ {
+ key = SafeBitConverter.ToUInt64(span);
+ span = span.Slice(8);
+ }
+ else
+ {
+ switch (span.Length)
+ {
+ case 1:
+ {
+ key = span[0];
+ span = span.Slice(1);
+ break;
+ }
+
+ case 2:
+ {
+ key = SafeBitConverter.ToUInt16(span);
+ span = span.Slice(2);
+ break;
+ }
+
+ case 3:
+ {
+ var a = span[0];
+ var b = SafeBitConverter.ToUInt16(span.Slice(1));
+ key = a | (ulong)b << 8;
+ span = span.Slice(3);
+ break;
+ }
+
+ case 4:
+ {
+ key = SafeBitConverter.ToUInt32(span);
+ span = span.Slice(4);
+ break;
+ }
+
+ case 5:
+ {
+ var a = span[0];
+ var b = SafeBitConverter.ToUInt32(span.Slice(1));
+ key = a | (ulong)b << 8;
+ span = span.Slice(5);
+ break;
+ }
+
+ case 6:
+ {
+ ulong a = SafeBitConverter.ToUInt16(span);
+ ulong b = SafeBitConverter.ToUInt32(span.Slice(2));
+ key = a | (b << 16);
+ span = span.Slice(6);
+ break;
+ }
+
+ case 7:
+ {
+ var a = span[0];
+ var b = SafeBitConverter.ToUInt16(span.Slice(1));
+ var c = SafeBitConverter.ToUInt32(span.Slice(3));
+ key = a | (ulong)b << 8 | (ulong)c << 24;
+ span = span.Slice(7);
+ break;
+ }
+
+ default:
+ throw new MessagePackSerializationException("Not Supported Length");
+ }
+ }
+
+ return key;
+ }
+ }
+ }
+}
diff --git a/tests/MessagePack.Generator.Tests/MessagePack.Generator.Tests.csproj b/tests/MessagePack.Generator.Tests/MessagePack.Generator.Tests.csproj
index d8b1a4c83..173496d34 100644
--- a/tests/MessagePack.Generator.Tests/MessagePack.Generator.Tests.csproj
+++ b/tests/MessagePack.Generator.Tests/MessagePack.Generator.Tests.csproj
@@ -14,7 +14,7 @@
-
+
From 4763459c0862bdf37931a387369bcc40c8491d16 Mon Sep 17 00:00:00 2001
From: Andrew Arnott
Date: Mon, 20 Mar 2023 18:04:49 -0600
Subject: [PATCH 016/660] Rollback Roslyn3 to 3.9
It presumably should be 3.8, but it doesn't compile with 3.8 at this point.
---
Directory.Packages.props | 1 -
.../MessagePack.Generator.Roslyn3.csproj | 3 +++
src/MessagePack.Generator.Roslyn3/MessagepackGenerator.cs | 4 ++--
3 files changed, 5 insertions(+), 3 deletions(-)
diff --git a/Directory.Packages.props b/Directory.Packages.props
index a1f151d30..68defe23d 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -7,7 +7,6 @@
0.13.5
-
4.3.0
4.5.0
diff --git a/src/MessagePack.Generator.Roslyn3/MessagePack.Generator.Roslyn3.csproj b/src/MessagePack.Generator.Roslyn3/MessagePack.Generator.Roslyn3.csproj
index b25ffbde0..57f0e9df9 100644
--- a/src/MessagePack.Generator.Roslyn3/MessagePack.Generator.Roslyn3.csproj
+++ b/src/MessagePack.Generator.Roslyn3/MessagePack.Generator.Roslyn3.csproj
@@ -7,6 +7,9 @@
enable
ROSLYN3
cs
+
+
+ 3.9.0
MessagePack.Generator
diff --git a/src/MessagePack.Generator.Roslyn3/MessagepackGenerator.cs b/src/MessagePack.Generator.Roslyn3/MessagepackGenerator.cs
index 316f914b3..cd0dbb0ab 100644
--- a/src/MessagePack.Generator.Roslyn3/MessagepackGenerator.cs
+++ b/src/MessagePack.Generator.Roslyn3/MessagepackGenerator.cs
@@ -23,12 +23,12 @@ public void Execute(GeneratorExecutionContext context)
return;
}
- var compiation = context.Compilation;
+ var compilation = context.Compilation;
var generateContext = new GeneratorContext(context);
foreach (var syntax in receiver.ClassDeclarations)
{
- Generate(syntax, compiation, generateContext);
+ Generate(syntax, compilation, generateContext);
}
}
From 7ede56cbc29812b402ddb55a1bf65f6afbaa4277 Mon Sep 17 00:00:00 2001
From: Andrew Arnott
Date: Mon, 20 Mar 2023 21:43:50 -0600
Subject: [PATCH 017/660] Got first new test passing
---
Directory.Packages.props | 1 +
nuget.config | 10 +
.../MessagepackGenerator.cs | 2 +-
.../MessagepackGenerator.Emit.cs | 2 +-
.../MessagepackGenerator.cs | 12 +-
.../GenerateEnumFormatterTest.cs | 178 +++++++++++++++---
.../MessagePack.Generator.Tests.csproj | 13 ++
tests/MessagePack.Generator.Tests/Usings.cs | 1 +
.../CSharpSourceGeneratorVerifier`1+Test.cs | 56 ++++++
.../Verifiers/ReferenceHelper.cs | 11 ++
10 files changed, 258 insertions(+), 28 deletions(-)
create mode 100644 tests/MessagePack.Generator.Tests/Usings.cs
create mode 100644 tests/MessagePack.Generator.Tests/Verifiers/CSharpSourceGeneratorVerifier`1+Test.cs
create mode 100644 tests/MessagePack.Generator.Tests/Verifiers/ReferenceHelper.cs
diff --git a/Directory.Packages.props b/Directory.Packages.props
index 68defe23d..2802d86ef 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -30,6 +30,7 @@
+
diff --git a/nuget.config b/nuget.config
index 2ed04eeb6..2f513c3f4 100644
--- a/nuget.config
+++ b/nuget.config
@@ -7,9 +7,19 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/src/MessagePack.Generator.Roslyn3/MessagepackGenerator.cs b/src/MessagePack.Generator.Roslyn3/MessagepackGenerator.cs
index cd0dbb0ab..3ef472a03 100644
--- a/src/MessagePack.Generator.Roslyn3/MessagepackGenerator.cs
+++ b/src/MessagePack.Generator.Roslyn3/MessagepackGenerator.cs
@@ -7,7 +7,7 @@
namespace MessagePack.Generator;
[Generator(LanguageNames.CSharp)]
-public partial class MessagepackGenerator : ISourceGenerator
+public partial class MessagePackGenerator : ISourceGenerator
{
public const string MessagePackObjectAttributeFullName = "MessagePack.MessagePackObjectAttribute";
diff --git a/src/MessagePack.Generator/MessagepackGenerator.Emit.cs b/src/MessagePack.Generator/MessagepackGenerator.Emit.cs
index 11c02d9cf..dbeb289ea 100644
--- a/src/MessagePack.Generator/MessagepackGenerator.Emit.cs
+++ b/src/MessagePack.Generator/MessagepackGenerator.Emit.cs
@@ -9,7 +9,7 @@
namespace MessagePack.Generator;
-public partial class MessagepackGenerator
+public partial class MessagePackGenerator
{
private static void Generate(TypeDeclarationSyntax syntax, Compilation compilation, IGeneratorContext context)
{
diff --git a/src/MessagePack.Generator/MessagepackGenerator.cs b/src/MessagePack.Generator/MessagepackGenerator.cs
index d0ce067a6..4435bf329 100644
--- a/src/MessagePack.Generator/MessagepackGenerator.cs
+++ b/src/MessagePack.Generator/MessagepackGenerator.cs
@@ -7,11 +7,16 @@
namespace MessagePack.Generator;
[Generator(LanguageNames.CSharp)]
-public partial class MessagepackGenerator : IIncrementalGenerator
+public partial class MessagePackGenerator : IIncrementalGenerator, ISourceGenerator
{
public const string MessagePackObjectAttributeFullName = "MessagePack.MessagePackObjectAttribute";
public const string MessagePackUnionAttributeFullName = "MessagePack.UnionAttribute";
+ public void Execute(GeneratorExecutionContext context)
+ {
+ throw new NotImplementedException();
+ }
+
public void Initialize(IncrementalGeneratorInitializationContext context)
{
var typeDeclarations = context.SyntaxProvider.ForAttributeWithMetadataName(
@@ -40,6 +45,11 @@ void Register(IncrementalValuesProvider typeDeclarations)
}
}
+ public void Initialize(GeneratorInitializationContext context)
+ {
+ throw new NotImplementedException();
+ }
+
private class Comparer : IEqualityComparer<(TypeDeclarationSyntax, Compilation)>
{
public static readonly Comparer Instance = new Comparer();
diff --git a/tests/MessagePack.Generator.Tests/GenerateEnumFormatterTest.cs b/tests/MessagePack.Generator.Tests/GenerateEnumFormatterTest.cs
index 2ad23fcbe..ce4d2098d 100644
--- a/tests/MessagePack.Generator.Tests/GenerateEnumFormatterTest.cs
+++ b/tests/MessagePack.Generator.Tests/GenerateEnumFormatterTest.cs
@@ -2,11 +2,15 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
+using System.Text;
using System.Threading;
using System.Threading.Tasks;
using FluentAssertions;
+using Microsoft.CodeAnalysis.Text;
+using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client;
using Xunit;
using Xunit.Abstractions;
+using VerifyCS = CSharpSourceGeneratorVerifier;
namespace MessagePack.Generator.Tests
{
@@ -22,42 +26,166 @@ public GenerateEnumFormatterTest(ITestOutputHelper testOutputHelper)
[Fact]
public async Task EnumFormatter()
{
- using var tempWorkarea = TemporaryProjectWorkarea.Create();
- var contents = @"
+ string contents = """
using System;
using System.Collections.Generic;
using MessagePack;
-namespace TempProject
+namespace TempProject;
+
+[MessagePackObject]
+public class MyMessagePackObject
+{
+ [Key(0)]
+ public MyEnum EnumValue { get; set; }
+}
+
+public enum MyEnum
+{
+ A, B, C
+}
+""";
+ string generated = """
+using System.Runtime.CompilerServices;
+using MessagePack;
+
+namespace Resolvers
{
- [MessagePackObject]
- public class MyMessagePackObject
+ partial class FormatterRegister
{
- [Key(0)]
- public MyEnum EnumValue { get; set; }
+ [ModuleInitializer]
+ internal static void TempProject_MyMessagePackObjectFormatterRegister()
+ {
+ MessagePack.Resolvers.StaticCompositeResolver.Instance.AddGeneratedFormatter(new global::Formatters.TempProject.MyEnumFormatter());
+ MessagePack.Resolvers.StaticCompositeResolver.Instance.AddGeneratedFormatter(new global::Formatters.TempProject.MyMessagePackObjectFormatter());
+ }
+ }
+}
+//
+
+#pragma warning disable 618
+#pragma warning disable 612
+#pragma warning disable 414
+#pragma warning disable 168
+#pragma warning disable CS1591 // document public APIs
+
+#pragma warning disable SA1403 // File may only contain a single namespace
+#pragma warning disable SA1649 // File name should match first type name
+
+namespace Formatters.TempProject
+{
+
+ public sealed class MyEnumFormatter : global::MessagePack.Formatters.IMessagePackFormatter
+ {
+ public void Serialize(ref global::MessagePack.MessagePackWriter writer, global::TempProject.MyEnum value, global::MessagePack.MessagePackSerializerOptions options)
+ {
+ writer.Write((global::System.Int32)value);
+ }
+
+ public global::TempProject.MyEnum Deserialize(ref global::MessagePack.MessagePackReader reader, global::MessagePack.MessagePackSerializerOptions options)
+ {
+ return (global::TempProject.MyEnum)reader.ReadInt32();
+ }
}
+}
+
+#pragma warning restore 168
+#pragma warning restore 414
+#pragma warning restore 618
+#pragma warning restore 612
+
+#pragma warning restore SA1403 // File may only contain a single namespace
+#pragma warning restore SA1649 // File name should match first type name
+
+
+//
+
+#pragma warning disable 618
+#pragma warning disable 612
+#pragma warning disable 414
+#pragma warning disable 168
+#pragma warning disable CS1591 // document public APIs
- public enum MyEnum
+#pragma warning disable SA1129 // Do not use default value type constructor
+#pragma warning disable SA1309 // Field names should not begin with underscore
+#pragma warning disable SA1312 // Variable names should begin with lower-case letter
+#pragma warning disable SA1403 // File may only contain a single namespace
+#pragma warning disable SA1649 // File name should match first type name
+
+namespace Formatters.TempProject
+{
+ public sealed class MyMessagePackObjectFormatter : global::MessagePack.Formatters.IMessagePackFormatter
{
- A, B, C
+
+ public void Serialize(ref global::MessagePack.MessagePackWriter writer, global::TempProject.MyMessagePackObject value, global::MessagePack.MessagePackSerializerOptions options)
+ {
+ if (value == null)
+ {
+ writer.WriteNil();
+ return;
+ }
+
+ global::MessagePack.IFormatterResolver formatterResolver = options.Resolver;
+ writer.WriteArrayHeader(1);
+ global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify(formatterResolver).Serialize(ref writer, value.EnumValue, options);
+ }
+
+ public global::TempProject.MyMessagePackObject Deserialize(ref global::MessagePack.MessagePackReader reader, global::MessagePack.MessagePackSerializerOptions options)
+ {
+ if (reader.TryReadNil())
+ {
+ return null;
+ }
+
+ options.Security.DepthStep(ref reader);
+ global::MessagePack.IFormatterResolver formatterResolver = options.Resolver;
+ var length = reader.ReadArrayHeader();
+ var ____result = new global::TempProject.MyMessagePackObject();
+
+ for (int i = 0; i < length; i++)
+ {
+ switch (i)
+ {
+ case 0:
+ ____result.EnumValue = global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify(formatterResolver).Deserialize(ref reader, options);
+ break;
+ default:
+ reader.Skip();
+ break;
+ }
+ }
+
+ reader.Depth--;
+ return ____result;
+ }
}
+
}
- ";
- tempWorkarea.AddFileToTargetProject("MyMessagePackObject.cs", contents);
-
- var compiler = new MessagePackCompiler.CodeGenerator(testOutputHelper.WriteLine, CancellationToken.None);
- await compiler.GenerateFileAsync(
- tempWorkarea.GetOutputCompilation().Compilation,
- tempWorkarea.OutputDirectory,
- "TempProjectResolver",
- "TempProject.Generated",
- false,
- string.Empty,
- Array.Empty());
-
- var compilation = tempWorkarea.GetOutputCompilation();
- var symbols = compilation.GetNamedTypeSymbolsFromGenerated();
- symbols.Should().Contain(x => x.Name == "MyEnumFormatter");
+
+#pragma warning restore 168
+#pragma warning restore 414
+#pragma warning restore 618
+#pragma warning restore 612
+
+#pragma warning restore SA1129 // Do not use default value type constructor
+#pragma warning restore SA1309 // Field names should not begin with underscore
+#pragma warning restore SA1312 // Variable names should begin with lower-case letter
+#pragma warning restore SA1403 // File may only contain a single namespace
+#pragma warning restore SA1649 // File name should match first type name
+
+
+""";
+ await new VerifyCS.Test
+ {
+ TestState =
+ {
+ Sources = { contents },
+ GeneratedSources =
+ {
+ (typeof(MessagePackGenerator), "TempProject.MyMessagePackObject.MessagePackFormatter.g.cs", SourceText.From(generated, Encoding.UTF8, SourceHashAlgorithm.Sha1)),
+ },
+ },
+ }.RunAsync();
}
}
}
diff --git a/tests/MessagePack.Generator.Tests/MessagePack.Generator.Tests.csproj b/tests/MessagePack.Generator.Tests/MessagePack.Generator.Tests.csproj
index 173496d34..7b621b055 100644
--- a/tests/MessagePack.Generator.Tests/MessagePack.Generator.Tests.csproj
+++ b/tests/MessagePack.Generator.Tests/MessagePack.Generator.Tests.csproj
@@ -2,10 +2,23 @@
net7.0
+ true
+ 11
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/MessagePack.Generator.Tests/Usings.cs b/tests/MessagePack.Generator.Tests/Usings.cs
new file mode 100644
index 000000000..67e549f86
--- /dev/null
+++ b/tests/MessagePack.Generator.Tests/Usings.cs
@@ -0,0 +1 @@
+global using System.Collections.Immutable;
diff --git a/tests/MessagePack.Generator.Tests/Verifiers/CSharpSourceGeneratorVerifier`1+Test.cs b/tests/MessagePack.Generator.Tests/Verifiers/CSharpSourceGeneratorVerifier`1+Test.cs
new file mode 100644
index 000000000..335166eeb
--- /dev/null
+++ b/tests/MessagePack.Generator.Tests/Verifiers/CSharpSourceGeneratorVerifier`1+Test.cs
@@ -0,0 +1,56 @@
+// Copyright (c) All contributors. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System.Collections.Immutable;
+using MessagePack;
+using MessagePack.Formatters;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Testing;
+using Microsoft.CodeAnalysis.Testing.Verifiers;
+
+public static partial class CSharpSourceGeneratorVerifier
+ where TSourceGenerator : ISourceGenerator, new()
+{
+ public class Test : CSharpSourceGeneratorTest
+ {
+ public Test()
+ {
+ this.ReferenceAssemblies = ReferenceHelper.DefaultReferences;
+
+ this.SolutionTransforms.Add((solution, projectId) =>
+ {
+ Project project = solution.GetProject(projectId);
+
+ project = project
+ .AddMetadataReference(MetadataReference.CreateFromFile(typeof(MessagePackObjectAttribute).Assembly.Location))
+ .AddMetadataReference(MetadataReference.CreateFromFile(typeof(IMessagePackFormatter).Assembly.Location));
+
+ return project.Solution;
+ });
+ }
+
+ protected override CompilationOptions CreateCompilationOptions()
+ {
+ CompilationOptions compilationOptions = base.CreateCompilationOptions();
+ return compilationOptions.WithSpecificDiagnosticOptions(
+ compilationOptions.SpecificDiagnosticOptions.SetItems(GetNullableWarningsFromCompiler()));
+ }
+
+ public LanguageVersion LanguageVersion { get; set; } = LanguageVersion.Latest;
+
+ private static ImmutableDictionary GetNullableWarningsFromCompiler()
+ {
+ string[] args = { "/warnaserror:nullable" };
+ CSharpCommandLineArguments commandLineArguments = CSharpCommandLineParser.Default.Parse(args, baseDirectory: Environment.CurrentDirectory, sdkDirectory: Environment.CurrentDirectory);
+ ImmutableDictionary nullableWarnings = commandLineArguments.CompilationOptions.SpecificDiagnosticOptions;
+
+ return nullableWarnings;
+ }
+
+ protected override ParseOptions CreateParseOptions()
+ {
+ return ((CSharpParseOptions)base.CreateParseOptions()).WithLanguageVersion(LanguageVersion);
+ }
+ }
+}
diff --git a/tests/MessagePack.Generator.Tests/Verifiers/ReferenceHelper.cs b/tests/MessagePack.Generator.Tests/Verifiers/ReferenceHelper.cs
new file mode 100644
index 000000000..b8b7104e8
--- /dev/null
+++ b/tests/MessagePack.Generator.Tests/Verifiers/ReferenceHelper.cs
@@ -0,0 +1,11 @@
+// Copyright (c) All contributors. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System.Reflection;
+using MessagePack;
+using Microsoft.CodeAnalysis.Testing;
+
+internal static class ReferenceHelper
+{
+ public static ReferenceAssemblies DefaultReferences = ReferenceAssemblies.Net.Net70;
+}
From 4d99531aba7d3be61b1a1c92511b484943b10f76 Mon Sep 17 00:00:00 2001
From: Andrew Arnott
Date: Tue, 21 Mar 2023 06:33:26 -0600
Subject: [PATCH 018/660] Fix package restore
---
Directory.Packages.props | 2 +-
nuget.config | 1 +
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/Directory.Packages.props b/Directory.Packages.props
index 2802d86ef..a272bdda2 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -35,7 +35,7 @@