Add New MessagePackGenerator Project
This commit is contained in:
Родитель
d272c765b7
Коммит
c485a750b7
|
@ -75,6 +75,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MessagePack.Internal", "src
|
|||
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("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MessagePack.GeneratorCore", "src\MessagePack.GeneratorCore\MessagePack.GeneratorCore.csproj", "{9962132D-A271-4E68-ACC1-18FA93462552}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MessagePack.Generator", "src\MessagePack.Generator\MessagePack.Generator.csproj", "{32C91908-5CAD-4C95-B240-ACBBACAC9476}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
@ -161,6 +165,14 @@ 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
|
||||
{32C91908-5CAD-4C95-B240-ACBBACAC9476}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
@ -186,6 +198,8 @@ Global
|
|||
{4142EA80-FEF4-44A5-8553-1AE84BEBAFED} = {51A614B0-E583-4DD2-AC7D-6A65634582E0}
|
||||
{C100FBA6-4164-4D6A-A532-5984D2B8DCB0} = {86309CF6-0054-4CE3-BFD3-CA0AA7DB17BC}
|
||||
{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}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {B3911209-2DBF-47F8-98F6-BBC0EDFE63DE}
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>netcoreapp2.2</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MicroBatchFramework" Version="1.5.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\MessagePack.GeneratorCore\MessagePack.GeneratorCore.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -0,0 +1,36 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using MicroBatchFramework;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
||||
namespace MessagePack.Generator
|
||||
{
|
||||
public class MessagepackCompiler : BatchBase
|
||||
{
|
||||
static async Task Main(string[] args)
|
||||
{
|
||||
await BatchHost.CreateDefaultBuilder().RunBatchEngineAsync<MessagepackCompiler>(args);
|
||||
}
|
||||
|
||||
public async Task RunAsync(
|
||||
[Option("i", "Input path of analyze csproj or directory, if input multiple csproj split with ','.")]string input,
|
||||
[Option("o", "Output file path(.cs) or directory(multiple generate file).")]string output,
|
||||
[Option("c", "Conditional compiler symbols, split with ','.")]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 multipleIfDiretiveOutputSymbols = null
|
||||
)
|
||||
{
|
||||
await new MessagePackCompiler.CodeGenerator(x => Console.WriteLine(x), this.Context.CancellationToken)
|
||||
.GenerateFileAsync(
|
||||
input,
|
||||
output,
|
||||
conditionalSymbol,
|
||||
resolverName,
|
||||
@namespace,
|
||||
useMapMode,
|
||||
multipleIfDiretiveOutputSymbols);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,212 @@
|
|||
// 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 System.Threading.Tasks;
|
||||
|
||||
#pragma warning disable SA1649 // File name should match first type name
|
||||
|
||||
namespace MessagePackCompiler.CodeAnalysis
|
||||
{
|
||||
public interface IResolverRegisterInfo
|
||||
{
|
||||
string FullName { get; }
|
||||
|
||||
string FormatterName { get; }
|
||||
}
|
||||
|
||||
public class ObjectSerializationInfo : IResolverRegisterInfo
|
||||
{
|
||||
public string Name { get; set; }
|
||||
|
||||
public string FullName { get; set; }
|
||||
|
||||
public string Namespace { get; set; }
|
||||
|
||||
public bool IsIntKey { get; set; }
|
||||
|
||||
public bool IsStringKey
|
||||
{
|
||||
get { return !this.IsIntKey; }
|
||||
}
|
||||
|
||||
public bool IsClass { get; set; }
|
||||
|
||||
public bool IsStruct
|
||||
{
|
||||
get { return !this.IsClass; }
|
||||
}
|
||||
|
||||
public MemberSerializationInfo[] ConstructorParameters { get; set; }
|
||||
|
||||
public MemberSerializationInfo[] Members { get; set; }
|
||||
|
||||
public bool HasIMessagePackSerializationCallbackReceiver { get; set; }
|
||||
|
||||
public bool NeedsCastOnBefore { get; set; }
|
||||
|
||||
public bool NeedsCastOnAfter { get; set; }
|
||||
|
||||
public string FormatterName => (this.Namespace == null ? this.Name : this.Namespace + "." + this.Name) + "Formatter";
|
||||
|
||||
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 class MemberSerializationInfo
|
||||
{
|
||||
public bool IsProperty { get; set; }
|
||||
|
||||
public bool IsField { get; set; }
|
||||
|
||||
public bool IsWritable { get; set; }
|
||||
|
||||
public bool IsReadable { get; set; }
|
||||
|
||||
public int IntKey { get; set; }
|
||||
|
||||
public string StringKey { get; set; }
|
||||
|
||||
public string Type { get; set; }
|
||||
|
||||
public string Name { get; set; }
|
||||
|
||||
public string ShortTypeName { get; set; }
|
||||
public string CustomFormatterTypeName { get; set; }
|
||||
|
||||
private readonly HashSet<string> primitiveTypes = new HashSet<string>(new string[]
|
||||
{
|
||||
"short",
|
||||
"int",
|
||||
"long",
|
||||
"ushort",
|
||||
"uint",
|
||||
"ulong",
|
||||
"float",
|
||||
"double",
|
||||
"bool",
|
||||
"byte",
|
||||
"sbyte",
|
||||
"char",
|
||||
////"global::System.DateTime",
|
||||
////"byte[]",
|
||||
////"string",
|
||||
});
|
||||
|
||||
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 $"formatterResolver.GetFormatterWithVerify<{this.Type}>().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))
|
||||
{
|
||||
return $"reader.Read{this.ShortTypeName.Replace("[]", "s")}()";
|
||||
}
|
||||
else
|
||||
{
|
||||
return $"formatterResolver.GetFormatterWithVerify<{this.Type}>().Deserialize(ref reader, options)";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class EnumSerializationInfo : IResolverRegisterInfo
|
||||
{
|
||||
public string Namespace { get; set; }
|
||||
|
||||
public string Name { get; set; }
|
||||
|
||||
public string FullName { get; set; }
|
||||
|
||||
public string UnderlyingType { get; set; }
|
||||
|
||||
public string FormatterName => (this.Namespace == null ? this.Name : this.Namespace + "." + this.Name) + "Formatter";
|
||||
}
|
||||
|
||||
public class GenericSerializationInfo : IResolverRegisterInfo, IEquatable<GenericSerializationInfo>
|
||||
{
|
||||
public string FullName { get; set; }
|
||||
|
||||
public string FormatterName { get; set; }
|
||||
|
||||
public bool Equals(GenericSerializationInfo other)
|
||||
{
|
||||
return this.FullName.Equals(other.FullName);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return this.FullName.GetHashCode();
|
||||
}
|
||||
}
|
||||
|
||||
public class UnionSerializationInfo : IResolverRegisterInfo
|
||||
{
|
||||
public string Namespace { get; set; }
|
||||
|
||||
public string Name { get; set; }
|
||||
|
||||
public string FullName { get; set; }
|
||||
|
||||
public string FormatterName => (this.Namespace == null ? this.Name : this.Namespace + "." + this.Name) + "Formatter";
|
||||
|
||||
public UnionSubTypeInfo[] SubTypes { get; set; }
|
||||
}
|
||||
|
||||
public class UnionSubTypeInfo
|
||||
{
|
||||
public string Type { get; set; }
|
||||
|
||||
public int Key { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,973 @@
|
|||
// 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 SA1509 // open braces that follow blank line
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.CodeAnalysis;
|
||||
|
||||
namespace MessagePackCompiler.CodeAnalysis
|
||||
{
|
||||
public class MessagePackGeneratorResolveFailedException : Exception
|
||||
{
|
||||
public MessagePackGeneratorResolveFailedException(string message)
|
||||
: base(message)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public class ReferenceSymbols
|
||||
{
|
||||
public readonly INamedTypeSymbol Task;
|
||||
public readonly INamedTypeSymbol TaskOfT;
|
||||
public readonly INamedTypeSymbol MessagePackObjectAttribute;
|
||||
public readonly INamedTypeSymbol UnionAttribute;
|
||||
public readonly INamedTypeSymbol SerializationConstructorAttribute;
|
||||
public readonly INamedTypeSymbol KeyAttribute;
|
||||
public readonly INamedTypeSymbol IgnoreAttribute;
|
||||
public readonly INamedTypeSymbol IgnoreDataMemberAttribute;
|
||||
public readonly INamedTypeSymbol IMessagePackSerializationCallbackReceiver;
|
||||
public readonly INamedTypeSymbol MessagePackFormatterAttribute;
|
||||
|
||||
public ReferenceSymbols(Compilation compilation, Action<string> 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");
|
||||
if (MessagePackObjectAttribute == null)
|
||||
{
|
||||
throw new InvalidOperationException("failed to get metadata of MessagePack.MessagePackObjectAttribute");
|
||||
}
|
||||
UnionAttribute = compilation.GetTypeByMetadataName("MessagePack.UnionAttribute");
|
||||
if (UnionAttribute == null)
|
||||
{
|
||||
throw new InvalidOperationException("failed to get metadata of MessagePack.UnionAttribute");
|
||||
}
|
||||
SerializationConstructorAttribute = compilation.GetTypeByMetadataName("MessagePack.SerializationConstructorAttribute");
|
||||
if (SerializationConstructorAttribute == null)
|
||||
{
|
||||
throw new InvalidOperationException("failed to get metadata of MessagePack.SerializationConstructorAttribute");
|
||||
}
|
||||
KeyAttribute = compilation.GetTypeByMetadataName("MessagePack.KeyAttribute");
|
||||
if (KeyAttribute == null)
|
||||
{
|
||||
throw new InvalidOperationException("failed to get metadata of MessagePack.KeyAttribute");
|
||||
}
|
||||
IgnoreAttribute = compilation.GetTypeByMetadataName("MessagePack.IgnoreMemberAttribute");
|
||||
if (IgnoreAttribute == null)
|
||||
{
|
||||
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");
|
||||
if (IMessagePackSerializationCallbackReceiver == null)
|
||||
{
|
||||
throw new InvalidOperationException("failed to get metadata of MessagePack.IMessagePackSerializationCallbackReceiver");
|
||||
}
|
||||
MessagePackFormatterAttribute = compilation.GetTypeByMetadataName("MessagePack.MessagePackFormatterAttribute");
|
||||
if (IMessagePackSerializationCallbackReceiver == null)
|
||||
{
|
||||
throw new InvalidOperationException("failed to get metadata of MessagePack.MessagePackFormatterAttribute");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class TypeCollector
|
||||
{
|
||||
const string CodegeneratorOnlyPreprocessorSymbol = "INCLUDE_ONLY_CODE_GENERATION";
|
||||
|
||||
static readonly SymbolDisplayFormat binaryWriteFormat = new SymbolDisplayFormat(
|
||||
genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters,
|
||||
miscellaneousOptions: SymbolDisplayMiscellaneousOptions.ExpandNullable,
|
||||
typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameOnly);
|
||||
|
||||
static readonly SymbolDisplayFormat shortTypeNameFormat = new SymbolDisplayFormat(
|
||||
typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypes);
|
||||
|
||||
readonly bool isForceUseMap;
|
||||
readonly ReferenceSymbols typeReferences;
|
||||
readonly INamedTypeSymbol[] targetTypes;
|
||||
readonly HashSet<string> embeddedTypes = new HashSet<string>(new string[]
|
||||
{
|
||||
"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<byte>",
|
||||
"System.ArraySegment<byte>?",
|
||||
|
||||
// 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",
|
||||
});
|
||||
|
||||
readonly Dictionary<string, string> knownGenericTypes = new Dictionary<string, string>
|
||||
{
|
||||
{"System.Collections.Generic.List<>", "global::MessagePack.Formatters.ListFormatter<TREPLACE>" },
|
||||
{"System.Collections.Generic.LinkedList<>", "global::MessagePack.Formatters.LinkedListFormatter<TREPLACE>"},
|
||||
{"System.Collections.Generic.Queue<>", "global::MessagePack.Formatters.QeueueFormatter<TREPLACE>"},
|
||||
{"System.Collections.Generic.Stack<>", "global::MessagePack.Formatters.StackFormatter<TREPLACE>"},
|
||||
{"System.Collections.Generic.HashSet<>", "global::MessagePack.Formatters.HashSetFormatter<TREPLACE>"},
|
||||
{"System.Collections.ObjectModel.ReadOnlyCollection<>", "global::MessagePack.Formatters.ReadOnlyCollectionFormatter<TREPLACE>"},
|
||||
{"System.Collections.Generic.IList<>", "global::MessagePack.Formatters.InterfaceListFormatter<TREPLACE>"},
|
||||
{"System.Collections.Generic.ICollection<>", "global::MessagePack.Formatters.InterfaceCollectionFormatter<TREPLACE>"},
|
||||
{"System.Collections.Generic.IEnumerable<>", "global::MessagePack.Formatters.InterfaceEnumerableFormatter<TREPLACE>"},
|
||||
{"System.Collections.Generic.Dictionary<,>", "global::MessagePack.Formatters.DictionaryFormatter<TREPLACE>"},
|
||||
{"System.Collections.Generic.IDictionary<,>", "global::MessagePack.Formatters.InterfaceDictionaryFormatter<TREPLACE>"},
|
||||
{"System.Collections.Generic.SortedDictionary<,>", "global::MessagePack.Formatters.SortedDictionaryFormatter<TREPLACE>"},
|
||||
{"System.Collections.Generic.SortedList<,>", "global::MessagePack.Formatters.SortedListFormatter<TREPLACE>"},
|
||||
{"System.Linq.ILookup<,>", "global::MessagePack.Formatters.InterfaceLookupFormatter<TREPLACE>"},
|
||||
{"System.Linq.IGrouping<,>", "global::MessagePack.Formatters.InterfaceGroupingFormatter<TREPLACE>"},
|
||||
{"System.Collections.ObjectModel.ObservableCollection<>", "global::MessagePack.Formatters.ObservableCollectionFormatter<TREPLACE>"},
|
||||
{"System.Collections.ObjectModel.ReadOnlyObservableCollection<>", "global::MessagePack.Formatters.ReadOnlyObservableCollectionFormatter<TREPLACE>" },
|
||||
{"System.Collections.Generic.IReadOnlyList<>", "global::MessagePack.Formatters.InterfaceReadOnlyListFormatter<TREPLACE>"},
|
||||
{"System.Collections.Generic.IReadOnlyCollection<>", "global::MessagePack.Formatters.InterfaceReadOnlyCollectionFormatter<TREPLACE>"},
|
||||
{"System.Collections.Generic.ISet<>", "global::MessagePack.Formatters.InterfaceSetFormatter<TREPLACE>"},
|
||||
{"System.Collections.Concurrent.ConcurrentBag<>", "global::MessagePack.Formatters.ConcurrentBagFormatter<TREPLACE>"},
|
||||
{"System.Collections.Concurrent.ConcurrentQueue<>", "global::MessagePack.Formatters.ConcurrentQueueFormatter<TREPLACE>"},
|
||||
{"System.Collections.Concurrent.ConcurrentStack<>", "global::MessagePack.Formatters.ConcurrentStackFormatter<TREPLACE>"},
|
||||
{"System.Collections.ObjectModel.ReadOnlyDictionary<,>", "global::MessagePack.Formatters.ReadOnlyDictionaryFormatter<TREPLACE>"},
|
||||
{"System.Collections.Generic.IReadOnlyDictionary<,>", "global::MessagePack.Formatters.InterfaceReadOnlyDictionaryFormatter<TREPLACE>"},
|
||||
{"System.Collections.Concurrent.ConcurrentDictionary<,>", "global::MessagePack.Formatters.ConcurrentDictionaryFormatter<TREPLACE>"},
|
||||
{"System.Lazy<>", "global::MessagePack.Formatters.LazyFormatter<TREPLACE>"},
|
||||
{"System.Threading.Tasks<>", "global::MessagePack.Formatters.TaskValueFormatter<TREPLACE>"},
|
||||
|
||||
{"System.Tuple<>", "global::MessagePack.Formatters.TupleFormatter<TREPLACE>"},
|
||||
{"System.Tuple<,>", "global::MessagePack.Formatters.TupleFormatter<TREPLACE>"},
|
||||
{"System.Tuple<,,>", "global::MessagePack.Formatters.TupleFormatter<TREPLACE>"},
|
||||
{"System.Tuple<,,,>", "global::MessagePack.Formatters.TupleFormatter<TREPLACE>"},
|
||||
{"System.Tuple<,,,,>", "global::MessagePack.Formatters.TupleFormatter<TREPLACE>"},
|
||||
{"System.Tuple<,,,,,>", "global::MessagePack.Formatters.TupleFormatter<TREPLACE>"},
|
||||
{"System.Tuple<,,,,,,>", "global::MessagePack.Formatters.TupleFormatter<TREPLACE>"},
|
||||
{"System.Tuple<,,,,,,,>", "global::MessagePack.Formatters.TupleFormatter<TREPLACE>"},
|
||||
|
||||
{"System.ValueTuple<>", "global::MessagePack.Formatters.ValueTupleFormatter<TREPLACE>"},
|
||||
{"System.ValueTuple<,>", "global::MessagePack.Formatters.ValueTupleFormatter<TREPLACE>"},
|
||||
{"System.ValueTuple<,,>", "global::MessagePack.Formatters.ValueTupleFormatter<TREPLACE>"},
|
||||
{"System.ValueTuple<,,,>", "global::MessagePack.Formatters.ValueTupleFormatter<TREPLACE>"},
|
||||
{"System.ValueTuple<,,,,>", "global::MessagePack.Formatters.ValueTupleFormatter<TREPLACE>"},
|
||||
{"System.ValueTuple<,,,,,>", "global::MessagePack.Formatters.ValueTupleFormatter<TREPLACE>"},
|
||||
{"System.ValueTuple<,,,,,,>", "global::MessagePack.Formatters.ValueTupleFormatter<TREPLACE>"},
|
||||
{"System.ValueTuple<,,,,,,,>", "global::MessagePack.Formatters.ValueTupleFormatter<TREPLACE>"},
|
||||
|
||||
{"System.Collections.Generic.KeyValuePair<,>", "global::MessagePack.Formatters.KeyValuePairFormatter<TREPLACE>"},
|
||||
{"System.Threading.Tasks.ValueTask<>", "global::MessagePack.Formatters.KeyValuePairFormatter<TREPLACE>"},
|
||||
{"System.ArraySegment<>", "global::MessagePack.Formatters.ArraySegmentFormatter<TREPLACE>"},
|
||||
|
||||
// extensions
|
||||
|
||||
{"System.Collections.Immutable.ImmutableArray<>", "global::MessagePack.ImmutableCollection.ImmutableArrayFormatter<TREPLACE>"},
|
||||
{"System.Collections.Immutable.ImmutableList<>", "global::MessagePack.ImmutableCollection.ImmutableListFormatter<TREPLACE>"},
|
||||
{"System.Collections.Immutable.ImmutableDictionary<,>", "global::MessagePack.ImmutableCollection.ImmutableDictionaryFormatter<TREPLACE>"},
|
||||
{"System.Collections.Immutable.ImmutableHashSet<>", "global::MessagePack.ImmutableCollection.ImmutableHashSetFormatter<TREPLACE>"},
|
||||
{"System.Collections.Immutable.ImmutableSortedDictionary<,>", "global::MessagePack.ImmutableCollection.ImmutableSortedDictionaryFormatter<TREPLACE>"},
|
||||
{"System.Collections.Immutable.ImmutableSortedSet<>", "global::MessagePack.ImmutableCollection.ImmutableSortedSetFormatter<TREPLACE>"},
|
||||
{"System.Collections.Immutable.ImmutableQueue<>", "global::MessagePack.ImmutableCollection.ImmutableQueueFormatter<TREPLACE>"},
|
||||
{"System.Collections.Immutable.ImmutableStack<>", "global::MessagePack.ImmutableCollection.ImmutableStackFormatter<TREPLACE>"},
|
||||
{"System.Collections.Immutable.IImmutableList<>", "global::MessagePack.ImmutableCollection.InterfaceImmutableListFormatter<TREPLACE>"},
|
||||
{"System.Collections.Immutable.IImmutableDictionary<,>", "global::MessagePack.ImmutableCollection.InterfaceImmutableDictionaryFormatter<TREPLACE>"},
|
||||
{"System.Collections.Immutable.IImmutableQueue<>", "global::MessagePack.ImmutableCollection.InterfaceImmutableQueueFormatter<TREPLACE>"},
|
||||
{"System.Collections.Immutable.IImmutableSet<>", "global::MessagePack.ImmutableCollection.InterfaceImmutableSetFormatter<TREPLACE>"},
|
||||
{"System.Collections.Immutable.IImmutableStack<>", "global::MessagePack.ImmutableCollection.InterfaceImmutableStackFormatter<TREPLACE>"},
|
||||
|
||||
{"Reactive.Bindings.ReactiveProperty<>", "global::MessagePack.ReactivePropertyExtension.ReactivePropertyFormatter<TREPLACE>"},
|
||||
{"Reactive.Bindings.IReactiveProperty<>", "global::MessagePack.ReactivePropertyExtension.InterfaceReactivePropertyFormatter<TREPLACE>"},
|
||||
{"Reactive.Bindings.IReadOnlyReactiveProperty<>", "global::MessagePack.ReactivePropertyExtension.InterfaceReadOnlyReactivePropertyFormatter<TREPLACE>"},
|
||||
{"Reactive.Bindings.ReactiveCollection<>", "global::MessagePack.ReactivePropertyExtension.ReactiveCollectionFormatter<TREPLACE>"},
|
||||
};
|
||||
|
||||
readonly Action<string> logger;
|
||||
|
||||
readonly bool disallowInternal;
|
||||
|
||||
// visitor workspace:
|
||||
private HashSet<ITypeSymbol> alreadyCollected;
|
||||
private List<ObjectSerializationInfo> collectedObjectInfo;
|
||||
private List<EnumSerializationInfo> collectedEnumInfo;
|
||||
private List<GenericSerializationInfo> collectedGenericInfo;
|
||||
private List<UnionSerializationInfo> collectedUnionInfo;
|
||||
|
||||
public TypeCollector(Compilation compilation, bool disallowInternal, bool isForceUseMap, Action<string> logger)
|
||||
{
|
||||
this.logger = logger;
|
||||
this.typeReferences = new ReferenceSymbols(compilation, logger);
|
||||
this.disallowInternal = disallowInternal;
|
||||
this.isForceUseMap = isForceUseMap;
|
||||
|
||||
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 == typeReferences.UnionAttribute))
|
||||
|| ((x.TypeKind == TypeKind.Class && x.IsAbstract) && x.GetAttributes().Any(x2 => x2.AttributeClass == typeReferences.UnionAttribute))
|
||||
|| ((x.TypeKind == TypeKind.Class) && x.GetAttributes().Any(x2 => x2.AttributeClass == typeReferences.MessagePackObjectAttribute))
|
||||
|| ((x.TypeKind == TypeKind.Struct) && x.GetAttributes().Any(x2 => x2.AttributeClass == typeReferences.MessagePackObjectAttribute))
|
||||
)
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
private void ResetWorkspace()
|
||||
{
|
||||
this.alreadyCollected = new HashSet<ITypeSymbol>();
|
||||
this.collectedObjectInfo = new List<ObjectSerializationInfo>();
|
||||
this.collectedEnumInfo = new List<EnumSerializationInfo>();
|
||||
this.collectedGenericInfo = new List<GenericSerializationInfo>();
|
||||
this.collectedUnionInfo = new List<UnionSerializationInfo>();
|
||||
}
|
||||
|
||||
// EntryPoint
|
||||
public (ObjectSerializationInfo[] objectInfo, EnumSerializationInfo[] enumInfo, GenericSerializationInfo[] genericInfo, UnionSerializationInfo[] unionInfo) Collect()
|
||||
{
|
||||
this.ResetWorkspace();
|
||||
|
||||
foreach (INamedTypeSymbol item in this.targetTypes)
|
||||
{
|
||||
this.CollectCore(item);
|
||||
}
|
||||
|
||||
return (this.collectedObjectInfo.ToArray(), this.collectedEnumInfo.ToArray(), this.collectedGenericInfo.Distinct().ToArray(), this.collectedUnionInfo.ToArray());
|
||||
}
|
||||
|
||||
// Gate of recursive collect
|
||||
private void CollectCore(ITypeSymbol typeSymbol)
|
||||
{
|
||||
if (!this.alreadyCollected.Add(typeSymbol))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.embeddedTypes.Contains(typeSymbol.ToString()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeSymbol.TypeKind == TypeKind.Array)
|
||||
{
|
||||
this.CollectArray(typeSymbol as IArrayTypeSymbol);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.IsAllowAccessibility(typeSymbol))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var type = typeSymbol as INamedTypeSymbol;
|
||||
|
||||
if (typeSymbol.TypeKind == TypeKind.Enum)
|
||||
{
|
||||
this.CollectEnum(type);
|
||||
return;
|
||||
}
|
||||
|
||||
if (type.IsGenericType)
|
||||
{
|
||||
this.CollectGeneric(type);
|
||||
return;
|
||||
}
|
||||
|
||||
if (type.TupleUnderlyingType != null)
|
||||
{
|
||||
CollectGeneric(type.TupleUnderlyingType);
|
||||
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);
|
||||
return;
|
||||
}
|
||||
|
||||
private void CollectEnum(INamedTypeSymbol type)
|
||||
{
|
||||
var info = new EnumSerializationInfo
|
||||
{
|
||||
Name = type.Name,
|
||||
Namespace = type.ContainingNamespace.IsGlobalNamespace ? null : type.ContainingNamespace.ToDisplayString(),
|
||||
FullName = type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat),
|
||||
UnderlyingType = type.EnumUnderlyingType.ToDisplayString(binaryWriteFormat),
|
||||
};
|
||||
|
||||
this.collectedEnumInfo.Add(info);
|
||||
}
|
||||
|
||||
private void CollectUnion(INamedTypeSymbol type)
|
||||
{
|
||||
System.Collections.Immutable.ImmutableArray<TypedConstant>[] unionAttrs = type.GetAttributes().Where(x => x.AttributeClass == 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
|
||||
var info = new UnionSerializationInfo
|
||||
{
|
||||
Name = type.Name,
|
||||
Namespace = type.ContainingNamespace.IsGlobalNamespace ? null : type.ContainingNamespace.ToDisplayString(),
|
||||
FullName = type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat),
|
||||
SubTypes = unionAttrs.Select(x => new UnionSubTypeInfo
|
||||
{
|
||||
Key = (int)x[0].Value,
|
||||
Type = (x[1].Value as ITypeSymbol).ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat),
|
||||
}).OrderBy(x => x.Key).ToArray(),
|
||||
};
|
||||
|
||||
this.collectedUnionInfo.Add(info);
|
||||
}
|
||||
|
||||
private void CollectArray(IArrayTypeSymbol array)
|
||||
{
|
||||
ITypeSymbol elemType = array.ElementType;
|
||||
this.CollectCore(elemType);
|
||||
|
||||
var info = new GenericSerializationInfo
|
||||
{
|
||||
FullName = array.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat),
|
||||
};
|
||||
|
||||
if (array.IsSZArray)
|
||||
{
|
||||
info.FormatterName = $"global::MessagePack.Formatters.ArrayFormatter<{elemType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)}>";
|
||||
}
|
||||
else if (array.Rank == 2)
|
||||
{
|
||||
info.FormatterName = $"global::MessagePack.Formatters.TwoDimensionalArrayFormatter<{elemType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)}>";
|
||||
}
|
||||
else if (array.Rank == 3)
|
||||
{
|
||||
info.FormatterName = $"global::MessagePack.Formatters.ThreeDimensionalArrayFormatter<{elemType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)}>";
|
||||
}
|
||||
else if (array.Rank == 4)
|
||||
{
|
||||
info.FormatterName = $"global::MessagePack.Formatters.FourDimensionalArrayFormatter<{elemType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)}>";
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException("does not supports array dimension, " + info.FullName);
|
||||
}
|
||||
|
||||
this.collectedGenericInfo.Add(info);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
private void CollectGeneric(INamedTypeSymbol type)
|
||||
{
|
||||
INamedTypeSymbol genericType = type.ConstructUnboundGenericType();
|
||||
var genericTypeString = genericType.ToDisplayString();
|
||||
var fullName = type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
|
||||
|
||||
// special case
|
||||
if (fullName == "global::System.ArraySegment<byte>" || fullName == "global::System.ArraySegment<byte>?")
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// nullable
|
||||
if (genericTypeString == "T?")
|
||||
{
|
||||
this.CollectCore(type.TypeArguments[0]);
|
||||
|
||||
if (!this.embeddedTypes.Contains(type.TypeArguments[0].ToString()))
|
||||
{
|
||||
var info = new GenericSerializationInfo
|
||||
{
|
||||
FormatterName = $"global::MessagePack.Formatters.NullableFormatter<{type.TypeArguments[0].ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)}>",
|
||||
FullName = type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat),
|
||||
};
|
||||
|
||||
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
|
||||
{
|
||||
FormatterName = f,
|
||||
FullName = type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat),
|
||||
};
|
||||
|
||||
this.collectedGenericInfo.Add(info);
|
||||
|
||||
if (genericTypeString == "System.Linq.ILookup<,>")
|
||||
{
|
||||
formatter = this.knownGenericTypes["System.Linq.IGrouping<,>"];
|
||||
f = formatter.Replace("TREPLACE", typeArgs);
|
||||
|
||||
var groupingInfo = new GenericSerializationInfo
|
||||
{
|
||||
FormatterName = f,
|
||||
FullName = $"global::System.Linq.IGrouping<{typeArgs}>",
|
||||
};
|
||||
|
||||
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
|
||||
{
|
||||
FormatterName = f,
|
||||
FullName = $"global::System.Collections.Generic.IEnumerable<{typeArgs}>",
|
||||
};
|
||||
|
||||
this.collectedGenericInfo.Add(enumerableInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CollectObject(INamedTypeSymbol type)
|
||||
{
|
||||
var isClass = !type.IsValueType;
|
||||
|
||||
AttributeData contractAttr = type.GetAttributes().FirstOrDefault(x => x.AttributeClass == this.typeReferences.MessagePackObjectAttribute);
|
||||
if (contractAttr == null)
|
||||
{
|
||||
throw new MessagePackGeneratorResolveFailedException("Serialization Object must mark MessagePackObjectAttribute." + " type: " + type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat));
|
||||
}
|
||||
|
||||
var isIntKey = true;
|
||||
var intMembers = new Dictionary<int, MemberSerializationInfo>();
|
||||
var stringMembers = new Dictionary<string, MemberSerializationInfo>();
|
||||
|
||||
if (this.isForceUseMap || (bool)contractAttr.ConstructorArguments[0].Value)
|
||||
{
|
||||
// All public members are serialize target except [Ignore] member.
|
||||
isIntKey = false;
|
||||
|
||||
var hiddenIntKey = 0;
|
||||
|
||||
foreach (IPropertySymbol item in type.GetAllMembers().OfType<IPropertySymbol>().Where(x => !x.IsOverride))
|
||||
{
|
||||
if (item.GetAttributes().Any(x => x.AttributeClass == this.typeReferences.IgnoreAttribute || x.AttributeClass.Name == this.typeReferences.IgnoreDataMemberAttribute.Name))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var customFormatterAttr = item.GetAttributes().FirstOrDefault(x => x.AttributeClass == this.typeReferences.MessagePackFormatterAttribute)?.ConstructorArguments[0].Value as INamedTypeSymbol;
|
||||
|
||||
var member = new MemberSerializationInfo
|
||||
{
|
||||
IsReadable = (item.GetMethod != null) && item.GetMethod.DeclaredAccessibility == Accessibility.Public && !item.IsStatic,
|
||||
IsWritable = (item.SetMethod != null) && item.SetMethod.DeclaredAccessibility == Accessibility.Public && !item.IsStatic,
|
||||
StringKey = item.Name,
|
||||
IsProperty = true,
|
||||
IsField = false,
|
||||
Name = item.Name,
|
||||
Type = item.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat),
|
||||
ShortTypeName = item.Type.ToDisplayString(binaryWriteFormat),
|
||||
CustomFormatterTypeName = customFormatterAttr?.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)
|
||||
};
|
||||
if (!member.IsReadable && !member.IsWritable)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
member.IntKey = hiddenIntKey++;
|
||||
stringMembers.Add(member.StringKey, member);
|
||||
|
||||
this.CollectCore(item.Type); // recursive collect
|
||||
}
|
||||
|
||||
foreach (IFieldSymbol item in type.GetAllMembers().OfType<IFieldSymbol>())
|
||||
{
|
||||
if (item.GetAttributes().Any(x => x.AttributeClass == this.typeReferences.IgnoreAttribute || x.AttributeClass.Name == this.typeReferences.IgnoreDataMemberAttribute.Name))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (item.IsImplicitlyDeclared)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var customFormatterAttr = item.GetAttributes().FirstOrDefault(x => x.AttributeClass == this.typeReferences.MessagePackFormatterAttribute)?.ConstructorArguments[0].Value as INamedTypeSymbol;
|
||||
|
||||
var member = new MemberSerializationInfo
|
||||
{
|
||||
IsReadable = item.DeclaredAccessibility == Accessibility.Public && !item.IsStatic,
|
||||
IsWritable = item.DeclaredAccessibility == Accessibility.Public && !item.IsReadOnly && !item.IsStatic,
|
||||
StringKey = item.Name,
|
||||
IsProperty = false,
|
||||
IsField = true,
|
||||
Name = item.Name,
|
||||
Type = item.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat),
|
||||
ShortTypeName = item.Type.ToDisplayString(binaryWriteFormat),
|
||||
CustomFormatterTypeName = customFormatterAttr?.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)
|
||||
};
|
||||
if (!member.IsReadable && !member.IsWritable)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
member.IntKey = hiddenIntKey++;
|
||||
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<IPropertySymbol>())
|
||||
{
|
||||
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 => x.AttributeClass == this.typeReferences.IgnoreAttribute || x.AttributeClass == this.typeReferences.IgnoreDataMemberAttribute))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var customFormatterAttr = item.GetAttributes().FirstOrDefault(x => x.AttributeClass == this.typeReferences.MessagePackFormatterAttribute)?.ConstructorArguments[0].Value as INamedTypeSymbol;
|
||||
|
||||
var member = new MemberSerializationInfo
|
||||
{
|
||||
IsReadable = (item.GetMethod != null) && item.GetMethod.DeclaredAccessibility == Accessibility.Public && !item.IsStatic,
|
||||
IsWritable = (item.SetMethod != null) && item.SetMethod.DeclaredAccessibility == Accessibility.Public && !item.IsStatic,
|
||||
IsProperty = true,
|
||||
IsField = false,
|
||||
Name = item.Name,
|
||||
Type = item.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat),
|
||||
ShortTypeName = item.Type.ToDisplayString(binaryWriteFormat),
|
||||
CustomFormatterTypeName = customFormatterAttr?.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)
|
||||
};
|
||||
if (!member.IsReadable && !member.IsWritable)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
TypedConstant? key = item.GetAttributes().FirstOrDefault(x => x.AttributeClass == this.typeReferences.KeyAttribute)?.ConstructorArguments[0];
|
||||
if (key == null)
|
||||
{
|
||||
throw new MessagePackGeneratorResolveFailedException("all public members must mark KeyAttribute or IgnoreMemberAttribute." + " type: " + type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) + " member:" + item.Name);
|
||||
}
|
||||
|
||||
var intKey = (key.Value.Value is int) ? (int)key.Value.Value : (int?)null;
|
||||
var stringKey = (key.Value.Value is string) ? (string)key.Value.Value : (string)null;
|
||||
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)
|
||||
{
|
||||
member.IntKey = (int)intKey;
|
||||
if (intMembers.ContainsKey(member.IntKey))
|
||||
{
|
||||
throw new MessagePackGeneratorResolveFailedException("key is duplicated, all members key must be unique." + " type: " + type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) + " member:" + item.Name);
|
||||
}
|
||||
|
||||
intMembers.Add(member.IntKey, member);
|
||||
}
|
||||
else
|
||||
{
|
||||
member.StringKey = (string)stringKey;
|
||||
if (stringMembers.ContainsKey(member.StringKey))
|
||||
{
|
||||
throw new MessagePackGeneratorResolveFailedException("key is duplicated, all members key must be unique." + " type: " + type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) + " member:" + item.Name);
|
||||
}
|
||||
|
||||
member.IntKey = hiddenIntKey++;
|
||||
stringMembers.Add(member.StringKey, member);
|
||||
}
|
||||
|
||||
this.CollectCore(item.Type); // recursive collect
|
||||
}
|
||||
|
||||
foreach (IFieldSymbol item in type.GetAllMembers().OfType<IFieldSymbol>())
|
||||
{
|
||||
if (item.IsImplicitlyDeclared)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (item.GetAttributes().Any(x => x.AttributeClass == this.typeReferences.IgnoreAttribute))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var customFormatterAttr = item.GetAttributes().FirstOrDefault(x => x.AttributeClass == this.typeReferences.MessagePackFormatterAttribute)?.ConstructorArguments[0].Value as INamedTypeSymbol;
|
||||
|
||||
var member = new MemberSerializationInfo
|
||||
{
|
||||
IsReadable = item.DeclaredAccessibility == Accessibility.Public && !item.IsStatic,
|
||||
IsWritable = item.DeclaredAccessibility == Accessibility.Public && !item.IsReadOnly && !item.IsStatic,
|
||||
IsProperty = true,
|
||||
IsField = false,
|
||||
Name = item.Name,
|
||||
Type = item.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat),
|
||||
ShortTypeName = item.Type.ToDisplayString(binaryWriteFormat),
|
||||
CustomFormatterTypeName = customFormatterAttr?.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)
|
||||
};
|
||||
if (!member.IsReadable && !member.IsWritable)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
TypedConstant? key = item.GetAttributes().FirstOrDefault(x => x.AttributeClass == this.typeReferences.KeyAttribute)?.ConstructorArguments[0];
|
||||
if (key == null)
|
||||
{
|
||||
throw new MessagePackGeneratorResolveFailedException("all public members must mark KeyAttribute or IgnoreMemberAttribute." + " type: " + type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) + " member:" + item.Name);
|
||||
}
|
||||
|
||||
var intKey = (key.Value.Value is int) ? (int)key.Value.Value : (int?)null;
|
||||
var stringKey = (key.Value.Value is string) ? (string)key.Value.Value : (string)null;
|
||||
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)
|
||||
{
|
||||
member.IntKey = (int)intKey;
|
||||
if (intMembers.ContainsKey(member.IntKey))
|
||||
{
|
||||
throw new MessagePackGeneratorResolveFailedException("key is duplicated, all members key must be unique." + " type: " + type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) + " member:" + item.Name);
|
||||
}
|
||||
|
||||
intMembers.Add(member.IntKey, member);
|
||||
}
|
||||
else
|
||||
{
|
||||
member.StringKey = (string)stringKey;
|
||||
if (stringMembers.ContainsKey(member.StringKey))
|
||||
{
|
||||
throw new MessagePackGeneratorResolveFailedException("key is duplicated, all members key must be unique." + " type: " + type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) + " member:" + item.Name);
|
||||
}
|
||||
|
||||
member.IntKey = hiddenIntKey++;
|
||||
stringMembers.Add(member.StringKey, member);
|
||||
}
|
||||
|
||||
this.CollectCore(item.Type); // recursive collect
|
||||
}
|
||||
}
|
||||
|
||||
// GetConstructor
|
||||
IEnumerator<IMethodSymbol> ctorEnumerator = null;
|
||||
IMethodSymbol ctor = type.Constructors.Where(x => x.DeclaredAccessibility == Accessibility.Public).SingleOrDefault(x => x.GetAttributes().Any(y => y.AttributeClass == 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<MemberSerializationInfo>();
|
||||
if (ctor != null)
|
||||
{
|
||||
ILookup<string, KeyValuePair<string, MemberSerializationInfo>> 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 + " paramterType:" + 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<KeyValuePair<string, MemberSerializationInfo>> hasKey = constructorLookupDictionary[item.Name];
|
||||
var len = hasKey.Count();
|
||||
if (len != 0)
|
||||
{
|
||||
if (len != 1)
|
||||
{
|
||||
if (ctorEnumerator != null)
|
||||
{
|
||||
ctor = null;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new MessagePackGeneratorResolveFailedException("duplicate matched constructor parameter name:" + type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) + " parameterName:" + item.Name + " paramterType:" + item.Type.Name);
|
||||
}
|
||||
}
|
||||
|
||||
paramMember = hasKey.First().Value;
|
||||
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) + " parameterName:" + item.Name + " paramterType:" + 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) + " parameterName:" + item.Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 == 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 = isClass,
|
||||
ConstructorParameters = constructorParameters.ToArray(),
|
||||
IsIntKey = isIntKey,
|
||||
Members = isIntKey ? intMembers.Values.ToArray() : stringMembers.Values.ToArray(),
|
||||
Name = type.ToDisplayString(shortTypeNameFormat).Replace(".", "_"),
|
||||
FullName = type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat),
|
||||
Namespace = type.ContainingNamespace.IsGlobalNamespace ? null : type.ContainingNamespace.ToDisplayString(),
|
||||
HasIMessagePackSerializationCallbackReceiver = hasSerializationConstructor,
|
||||
NeedsCastOnAfter = needsCastOnAfter,
|
||||
NeedsCastOnBefore = needsCastOnBefore,
|
||||
};
|
||||
this.collectedObjectInfo.Add(info);
|
||||
}
|
||||
|
||||
private static bool TryGetNextConstructor(IEnumerator<IMethodSymbol> 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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,224 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MessagePackCompiler.CodeAnalysis;
|
||||
using MessagePackCompiler.Generator;
|
||||
|
||||
namespace MessagePackCompiler
|
||||
{
|
||||
public class CodeGenerator
|
||||
{
|
||||
static readonly Encoding NoBomUtf8 = new UTF8Encoding(false);
|
||||
|
||||
Action<string> logger;
|
||||
CancellationToken cancellationToken;
|
||||
|
||||
public CodeGenerator(Action<string> logger, CancellationToken cancellationToken)
|
||||
{
|
||||
this.logger = logger;
|
||||
this.cancellationToken = cancellationToken;
|
||||
}
|
||||
|
||||
public async Task GenerateFileAsync(
|
||||
string input,
|
||||
string output,
|
||||
string conditionalSymbol,
|
||||
string resolverName,
|
||||
string @namespace,
|
||||
bool useMapMode,
|
||||
string multipleIfDiretiveOutputSymbols
|
||||
)
|
||||
{
|
||||
var namespaceDot = string.IsNullOrWhiteSpace(@namespace) ? "" : @namespace + ".";
|
||||
var conditionalSymbols = conditionalSymbol?.Split(',') ?? Array.Empty<string>();
|
||||
var multipleOutputSymbols = multipleIfDiretiveOutputSymbols?.Split(',') ?? Array.Empty<string>();
|
||||
|
||||
var sw = Stopwatch.StartNew();
|
||||
|
||||
foreach (var multioutSymbol in multipleOutputSymbols.Length == 0 ? new[] { "" } : multipleOutputSymbols)
|
||||
{
|
||||
logger("Project Compilation Start:" + input);
|
||||
|
||||
var compilation = (Path.GetExtension(input) == ".csproj")
|
||||
? await MessagePackCompilation.CreateFromProjectAsync(input.Split(','), conditionalSymbols.Concat(new[] { multioutSymbol }).ToArray(), cancellationToken)
|
||||
: await MessagePackCompilation.CreateFromDirectoryAsync(input, conditionalSymbols.Concat(new[] { multioutSymbol }).ToArray(), cancellationToken);
|
||||
var collector = new TypeCollector(compilation, true, useMapMode, x => Console.WriteLine(x));
|
||||
|
||||
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 objectFormatterTemplates = objectInfo
|
||||
.GroupBy(x => x.Namespace)
|
||||
.Select(x => new FormatterTemplate()
|
||||
{
|
||||
Namespace = namespaceDot + "Formatters" + ((x.Key == null) ? "" : "." + x.Key),
|
||||
ObjectSerializationInfos = x.ToArray(),
|
||||
})
|
||||
.ToArray();
|
||||
|
||||
var enumFormatterTemplates = enumInfo
|
||||
.GroupBy(x => x.Namespace)
|
||||
.Select(x => new EnumTemplate()
|
||||
{
|
||||
Namespace = namespaceDot + "Formatters" + ((x.Key == null) ? "" : "." + x.Key),
|
||||
EnumSerializationInfos = x.ToArray()
|
||||
})
|
||||
.ToArray();
|
||||
|
||||
var unionFormatterTemplates = unionInfo
|
||||
.GroupBy(x => x.Namespace)
|
||||
.Select(x => new UnionTemplate()
|
||||
{
|
||||
Namespace = namespaceDot + "Formatters" + ((x.Key == null) ? "" : "." + x.Key),
|
||||
UnionSerializationInfos = x.ToArray()
|
||||
})
|
||||
.ToArray();
|
||||
|
||||
var resolverTemplate = new ResolverTemplate()
|
||||
{
|
||||
Namespace = namespaceDot + "Resolvers",
|
||||
FormatterNamespace = namespaceDot + "Formatters",
|
||||
ResolverName = resolverName,
|
||||
RegisterInfos = genericInfo.Cast<IResolverRegisterInfo>().Concat(enumInfo).Concat(unionInfo).Concat(objectInfo).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);
|
||||
}
|
||||
|
||||
if (multioutSymbol == "")
|
||||
{
|
||||
await OutputAsync(output, sb.ToString(), cancellationToken);
|
||||
}
|
||||
else
|
||||
{
|
||||
var fname = Path.GetFileNameWithoutExtension(output) + "." + MultiSymbolToSafeFilePath(multioutSymbol) + ".cs";
|
||||
var text = $"#if {multioutSymbol}" + Environment.NewLine + sb.ToString() + Environment.NewLine + "#endif";
|
||||
await OutputAsync(Path.Combine(Path.GetDirectoryName(output), fname), text, cancellationToken);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Multiple File output
|
||||
foreach (var x in objectInfo)
|
||||
{
|
||||
var template = new FormatterTemplate()
|
||||
{
|
||||
Namespace = namespaceDot + "Formatters" + ((x.Namespace == null) ? "" : "." + x.Namespace),
|
||||
ObjectSerializationInfos = new[] { x },
|
||||
};
|
||||
|
||||
var text = template.TransformText();
|
||||
await OutputToDirAsync(output, template.Namespace, x.Name + "Formatter", multioutSymbol, text, cancellationToken);
|
||||
}
|
||||
|
||||
foreach (var x in enumInfo)
|
||||
{
|
||||
var template = new EnumTemplate()
|
||||
{
|
||||
Namespace = namespaceDot + "Formatters" + ((x.Namespace == null) ? "" : "." + x.Namespace),
|
||||
EnumSerializationInfos = new[] { x },
|
||||
};
|
||||
|
||||
var text = template.TransformText();
|
||||
await OutputToDirAsync(output, template.Namespace, x.Name + "Formatter", multioutSymbol, text, cancellationToken);
|
||||
}
|
||||
|
||||
foreach (var x in unionInfo)
|
||||
{
|
||||
var template = new UnionTemplate()
|
||||
{
|
||||
Namespace = namespaceDot + "Formatters" + ((x.Namespace == null) ? "" : "." + x.Namespace),
|
||||
UnionSerializationInfos = new[] { x },
|
||||
};
|
||||
|
||||
var text = template.TransformText();
|
||||
await OutputToDirAsync(output, template.Namespace, x.Name + "Formatter", multioutSymbol, text, cancellationToken);
|
||||
}
|
||||
|
||||
var resolverTemplate = new ResolverTemplate()
|
||||
{
|
||||
Namespace = namespaceDot + "Resolvers",
|
||||
FormatterNamespace = namespaceDot + "Formatters",
|
||||
ResolverName = resolverName,
|
||||
RegisterInfos = genericInfo.Cast<IResolverRegisterInfo>().Concat(enumInfo).Concat(unionInfo).Concat(objectInfo).ToArray()
|
||||
};
|
||||
|
||||
await OutputToDirAsync(output, resolverTemplate.Namespace, resolverTemplate.ResolverName, multioutSymbol, resolverTemplate.TransformText(), cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
logger("Output Generation Complete:" + sw.Elapsed.ToString());
|
||||
}
|
||||
|
||||
Task OutputToDirAsync(string dir, string ns, string name, string multipleOutSymbol, string text, CancellationToken cancellationToken)
|
||||
{
|
||||
if (multipleOutSymbol == "")
|
||||
{
|
||||
return OutputAsync(Path.Combine(dir, $"{ns}_{name}".Replace(".", "_").Replace("global::", "") + ".cs"), text, cancellationToken);
|
||||
}
|
||||
else
|
||||
{
|
||||
text = $"#if {multipleOutSymbol}" + Environment.NewLine + text + Environment.NewLine + "#endif";
|
||||
return OutputAsync(Path.Combine(dir, MultiSymbolToSafeFilePath(multipleOutSymbol), $"{ns}_{name}".Replace(".", "_").Replace("global::", "") + ".cs"), text, cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
Task OutputAsync(string path, string text, CancellationToken cancellationToken)
|
||||
{
|
||||
path = path.Replace("global::", "");
|
||||
|
||||
const string prefix = "[Out]";
|
||||
logger(prefix + path);
|
||||
|
||||
var fi = new FileInfo(path);
|
||||
if (!fi.Directory.Exists)
|
||||
{
|
||||
fi.Directory.Create();
|
||||
}
|
||||
|
||||
System.IO.File.WriteAllText(path, text, NoBomUtf8);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
static string MultiSymbolToSafeFilePath(string symbol)
|
||||
{
|
||||
return symbol.Replace("!", "NOT_").Replace("(", "").Replace(")", "").Replace("||", "_OR_").Replace("&&", "_AND_");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,345 @@
|
|||
// ------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// このコードはツールによって生成されました。
|
||||
// ランタイム バージョン: 16.0.0.0
|
||||
//
|
||||
// このファイルへの変更は、正しくない動作の原因になる可能性があり、
|
||||
// コードが再生成されると失われます。
|
||||
// </auto-generated>
|
||||
// ------------------------------------------------------------------------------
|
||||
namespace MessagePackCompiler.Generator
|
||||
{
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
|
||||
/// <summary>
|
||||
/// Class to produce the template output
|
||||
/// </summary>
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "16.0.0.0")]
|
||||
public partial class EnumTemplate : EnumTemplateBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Create the template output
|
||||
/// </summary>
|
||||
public virtual string TransformText()
|
||||
{
|
||||
this.Write(@"#pragma warning disable 618
|
||||
#pragma warning disable 612
|
||||
#pragma warning disable 414
|
||||
#pragma warning disable 168
|
||||
|
||||
#pragma warning disable SA1200 // Using directives should be placed correctly
|
||||
#pragma warning disable SA1403 // File may only contain a single namespace
|
||||
#pragma warning disable SA1649 // File name should match first type name
|
||||
|
||||
namespace ");
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(Namespace));
|
||||
this.Write("\r\n{\r\n using System;\r\n using System.Buffers;\r\n using MessagePack;\r\n");
|
||||
foreach(var info in EnumSerializationInfos) {
|
||||
this.Write("\r\n public sealed class ");
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(info.Name));
|
||||
this.Write("Formatter : global::MessagePack.Formatters.IMessagePackFormatter<");
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(info.FullName));
|
||||
this.Write(">\r\n {\r\n public void Serialize(ref MessagePackWriter writer, ");
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(info.FullName));
|
||||
this.Write(" value, global::MessagePack.MessagePackSerializerOptions options)\r\n {\r\n " +
|
||||
" writer.Write((");
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(info.UnderlyingType));
|
||||
this.Write(")value);\r\n }\r\n\r\n public ");
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(info.FullName));
|
||||
this.Write(" Deserialize(ref MessagePackReader reader, global::MessagePack.MessagePackSeriali" +
|
||||
"zerOptions options)\r\n {\r\n return (");
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(info.FullName));
|
||||
this.Write(")reader.Read");
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(info.UnderlyingType));
|
||||
this.Write("();\r\n }\r\n }\r\n");
|
||||
}
|
||||
this.Write(@"}
|
||||
|
||||
#pragma warning restore 168
|
||||
#pragma warning restore 414
|
||||
#pragma warning restore 618
|
||||
#pragma warning restore 612
|
||||
|
||||
#pragma warning restore SA1200 // Using directives should be placed correctly
|
||||
#pragma warning restore SA1403 // File may only contain a single namespace
|
||||
#pragma warning restore SA1649 // File name should match first type name
|
||||
");
|
||||
return this.GenerationEnvironment.ToString();
|
||||
}
|
||||
}
|
||||
#region Base class
|
||||
/// <summary>
|
||||
/// Base class for this transformation
|
||||
/// </summary>
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "16.0.0.0")]
|
||||
public class EnumTemplateBase
|
||||
{
|
||||
#region Fields
|
||||
private global::System.Text.StringBuilder generationEnvironmentField;
|
||||
private global::System.CodeDom.Compiler.CompilerErrorCollection errorsField;
|
||||
private global::System.Collections.Generic.List<int> indentLengthsField;
|
||||
private string currentIndentField = "";
|
||||
private bool endsWithNewline;
|
||||
private global::System.Collections.Generic.IDictionary<string, object> sessionField;
|
||||
#endregion
|
||||
#region Properties
|
||||
/// <summary>
|
||||
/// The string builder that generation-time code is using to assemble generated output
|
||||
/// </summary>
|
||||
protected System.Text.StringBuilder GenerationEnvironment
|
||||
{
|
||||
get
|
||||
{
|
||||
if ((this.generationEnvironmentField == null))
|
||||
{
|
||||
this.generationEnvironmentField = new global::System.Text.StringBuilder();
|
||||
}
|
||||
return this.generationEnvironmentField;
|
||||
}
|
||||
set
|
||||
{
|
||||
this.generationEnvironmentField = value;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// The error collection for the generation process
|
||||
/// </summary>
|
||||
public System.CodeDom.Compiler.CompilerErrorCollection Errors
|
||||
{
|
||||
get
|
||||
{
|
||||
if ((this.errorsField == null))
|
||||
{
|
||||
this.errorsField = new global::System.CodeDom.Compiler.CompilerErrorCollection();
|
||||
}
|
||||
return this.errorsField;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// A list of the lengths of each indent that was added with PushIndent
|
||||
/// </summary>
|
||||
private System.Collections.Generic.List<int> indentLengths
|
||||
{
|
||||
get
|
||||
{
|
||||
if ((this.indentLengthsField == null))
|
||||
{
|
||||
this.indentLengthsField = new global::System.Collections.Generic.List<int>();
|
||||
}
|
||||
return this.indentLengthsField;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets the current indent we use when adding lines to the output
|
||||
/// </summary>
|
||||
public string CurrentIndent
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.currentIndentField;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Current transformation session
|
||||
/// </summary>
|
||||
public virtual global::System.Collections.Generic.IDictionary<string, object> Session
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.sessionField;
|
||||
}
|
||||
set
|
||||
{
|
||||
this.sessionField = value;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
#region Transform-time helpers
|
||||
/// <summary>
|
||||
/// Write text directly into the generated output
|
||||
/// </summary>
|
||||
public void Write(string textToAppend)
|
||||
{
|
||||
if (string.IsNullOrEmpty(textToAppend))
|
||||
{
|
||||
return;
|
||||
}
|
||||
// If we're starting off, or if the previous text ended with a newline,
|
||||
// we have to append the current indent first.
|
||||
if (((this.GenerationEnvironment.Length == 0)
|
||||
|| this.endsWithNewline))
|
||||
{
|
||||
this.GenerationEnvironment.Append(this.currentIndentField);
|
||||
this.endsWithNewline = false;
|
||||
}
|
||||
// Check if the current text ends with a newline
|
||||
if (textToAppend.EndsWith(global::System.Environment.NewLine, global::System.StringComparison.CurrentCulture))
|
||||
{
|
||||
this.endsWithNewline = true;
|
||||
}
|
||||
// This is an optimization. If the current indent is "", then we don't have to do any
|
||||
// of the more complex stuff further down.
|
||||
if ((this.currentIndentField.Length == 0))
|
||||
{
|
||||
this.GenerationEnvironment.Append(textToAppend);
|
||||
return;
|
||||
}
|
||||
// Everywhere there is a newline in the text, add an indent after it
|
||||
textToAppend = textToAppend.Replace(global::System.Environment.NewLine, (global::System.Environment.NewLine + this.currentIndentField));
|
||||
// If the text ends with a newline, then we should strip off the indent added at the very end
|
||||
// because the appropriate indent will be added when the next time Write() is called
|
||||
if (this.endsWithNewline)
|
||||
{
|
||||
this.GenerationEnvironment.Append(textToAppend, 0, (textToAppend.Length - this.currentIndentField.Length));
|
||||
}
|
||||
else
|
||||
{
|
||||
this.GenerationEnvironment.Append(textToAppend);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Write text directly into the generated output
|
||||
/// </summary>
|
||||
public void WriteLine(string textToAppend)
|
||||
{
|
||||
this.Write(textToAppend);
|
||||
this.GenerationEnvironment.AppendLine();
|
||||
this.endsWithNewline = true;
|
||||
}
|
||||
/// <summary>
|
||||
/// Write formatted text directly into the generated output
|
||||
/// </summary>
|
||||
public void Write(string format, params object[] args)
|
||||
{
|
||||
this.Write(string.Format(global::System.Globalization.CultureInfo.CurrentCulture, format, args));
|
||||
}
|
||||
/// <summary>
|
||||
/// Write formatted text directly into the generated output
|
||||
/// </summary>
|
||||
public void WriteLine(string format, params object[] args)
|
||||
{
|
||||
this.WriteLine(string.Format(global::System.Globalization.CultureInfo.CurrentCulture, format, args));
|
||||
}
|
||||
/// <summary>
|
||||
/// Raise an error
|
||||
/// </summary>
|
||||
public void Error(string message)
|
||||
{
|
||||
System.CodeDom.Compiler.CompilerError error = new global::System.CodeDom.Compiler.CompilerError();
|
||||
error.ErrorText = message;
|
||||
this.Errors.Add(error);
|
||||
}
|
||||
/// <summary>
|
||||
/// Raise a warning
|
||||
/// </summary>
|
||||
public void Warning(string message)
|
||||
{
|
||||
System.CodeDom.Compiler.CompilerError error = new global::System.CodeDom.Compiler.CompilerError();
|
||||
error.ErrorText = message;
|
||||
error.IsWarning = true;
|
||||
this.Errors.Add(error);
|
||||
}
|
||||
/// <summary>
|
||||
/// Increase the indent
|
||||
/// </summary>
|
||||
public void PushIndent(string indent)
|
||||
{
|
||||
if ((indent == null))
|
||||
{
|
||||
throw new global::System.ArgumentNullException("indent");
|
||||
}
|
||||
this.currentIndentField = (this.currentIndentField + indent);
|
||||
this.indentLengths.Add(indent.Length);
|
||||
}
|
||||
/// <summary>
|
||||
/// Remove the last indent that was added with PushIndent
|
||||
/// </summary>
|
||||
public string PopIndent()
|
||||
{
|
||||
string returnValue = "";
|
||||
if ((this.indentLengths.Count > 0))
|
||||
{
|
||||
int indentLength = this.indentLengths[(this.indentLengths.Count - 1)];
|
||||
this.indentLengths.RemoveAt((this.indentLengths.Count - 1));
|
||||
if ((indentLength > 0))
|
||||
{
|
||||
returnValue = this.currentIndentField.Substring((this.currentIndentField.Length - indentLength));
|
||||
this.currentIndentField = this.currentIndentField.Remove((this.currentIndentField.Length - indentLength));
|
||||
}
|
||||
}
|
||||
return returnValue;
|
||||
}
|
||||
/// <summary>
|
||||
/// Remove any indentation
|
||||
/// </summary>
|
||||
public void ClearIndent()
|
||||
{
|
||||
this.indentLengths.Clear();
|
||||
this.currentIndentField = "";
|
||||
}
|
||||
#endregion
|
||||
#region ToString Helpers
|
||||
/// <summary>
|
||||
/// Utility class to produce culture-oriented representation of an object as a string.
|
||||
/// </summary>
|
||||
public class ToStringInstanceHelper
|
||||
{
|
||||
private System.IFormatProvider formatProviderField = global::System.Globalization.CultureInfo.InvariantCulture;
|
||||
/// <summary>
|
||||
/// Gets or sets format provider to be used by ToStringWithCulture method.
|
||||
/// </summary>
|
||||
public System.IFormatProvider FormatProvider
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.formatProviderField ;
|
||||
}
|
||||
set
|
||||
{
|
||||
if ((value != null))
|
||||
{
|
||||
this.formatProviderField = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// This is called from the compile/run appdomain to convert objects within an expression block to a string
|
||||
/// </summary>
|
||||
public string ToStringWithCulture(object objectToConvert)
|
||||
{
|
||||
if ((objectToConvert == null))
|
||||
{
|
||||
throw new global::System.ArgumentNullException("objectToConvert");
|
||||
}
|
||||
System.Type t = objectToConvert.GetType();
|
||||
System.Reflection.MethodInfo method = t.GetMethod("ToString", new System.Type[] {
|
||||
typeof(System.IFormatProvider)});
|
||||
if ((method == null))
|
||||
{
|
||||
return objectToConvert.ToString();
|
||||
}
|
||||
else
|
||||
{
|
||||
return ((string)(method.Invoke(objectToConvert, new object[] {
|
||||
this.formatProviderField })));
|
||||
}
|
||||
}
|
||||
}
|
||||
private ToStringInstanceHelper toStringHelperField = new ToStringInstanceHelper();
|
||||
/// <summary>
|
||||
/// Helper to produce culture-oriented representation of an object as a string
|
||||
/// </summary>
|
||||
public ToStringInstanceHelper ToStringHelper
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.toStringHelperField;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
#endregion
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
<#@ template debug="false" hostspecific="false" linePragmas="false" language="C#" #>
|
||||
<#@ assembly name="System.Core" #>
|
||||
<#@ import namespace="System.Linq" #>
|
||||
<#@ import namespace="System.Text" #>
|
||||
<#@ import namespace="System.Collections.Generic" #>
|
||||
#pragma warning disable 618
|
||||
#pragma warning disable 612
|
||||
#pragma warning disable 414
|
||||
#pragma warning disable 168
|
||||
|
||||
#pragma warning disable SA1200 // Using directives should be placed correctly
|
||||
#pragma warning disable SA1403 // File may only contain a single namespace
|
||||
#pragma warning disable SA1649 // File name should match first type name
|
||||
|
||||
namespace <#= Namespace #>
|
||||
{
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using MessagePack;
|
||||
<# foreach(var info in EnumSerializationInfos) { #>
|
||||
|
||||
public sealed class <#= info.Name #>Formatter : global::MessagePack.Formatters.IMessagePackFormatter<<#= info.FullName #>>
|
||||
{
|
||||
public void Serialize(ref MessagePackWriter writer, <#= info.FullName #> value, global::MessagePack.MessagePackSerializerOptions options)
|
||||
{
|
||||
writer.Write((<#= info.UnderlyingType #>)value);
|
||||
}
|
||||
|
||||
public <#= info.FullName #> Deserialize(ref MessagePackReader reader, global::MessagePack.MessagePackSerializerOptions options)
|
||||
{
|
||||
return (<#= info.FullName #>)reader.Read<#= info.UnderlyingType #>();
|
||||
}
|
||||
}
|
||||
<# } #>
|
||||
}
|
||||
|
||||
#pragma warning restore 168
|
||||
#pragma warning restore 414
|
||||
#pragma warning restore 618
|
||||
#pragma warning restore 612
|
||||
|
||||
#pragma warning restore SA1200 // Using directives should be placed correctly
|
||||
#pragma warning restore SA1403 // File may only contain a single namespace
|
||||
#pragma warning restore SA1649 // File name should match first type name
|
|
@ -0,0 +1,481 @@
|
|||
// ------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// このコードはツールによって生成されました。
|
||||
// ランタイム バージョン: 16.0.0.0
|
||||
//
|
||||
// このファイルへの変更は、正しくない動作の原因になる可能性があり、
|
||||
// コードが再生成されると失われます。
|
||||
// </auto-generated>
|
||||
// ------------------------------------------------------------------------------
|
||||
namespace MessagePackCompiler.Generator
|
||||
{
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
|
||||
/// <summary>
|
||||
/// Class to produce the template output
|
||||
/// </summary>
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "16.0.0.0")]
|
||||
public partial class FormatterTemplate : FormatterTemplateBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Create the template output
|
||||
/// </summary>
|
||||
public virtual string TransformText()
|
||||
{
|
||||
this.Write(@"#pragma warning disable 618
|
||||
#pragma warning disable 612
|
||||
#pragma warning disable 414
|
||||
#pragma warning disable 168
|
||||
|
||||
#pragma warning disable SA1129 // Do not use default value type constructor
|
||||
#pragma warning disable SA1200 // Using directives should be placed correctly
|
||||
#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 ");
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(Namespace));
|
||||
this.Write("\r\n{\r\n using System;\r\n using System.Buffers;\r\n using MessagePack;\r\n");
|
||||
foreach(var objInfo in ObjectSerializationInfos) {
|
||||
this.Write("\r\n public sealed class ");
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(objInfo.Name));
|
||||
this.Write("Formatter : global::MessagePack.Formatters.IMessagePackFormatter<");
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(objInfo.FullName));
|
||||
this.Write(">\r\n {\r\n");
|
||||
foreach(var item in objInfo.Members) {
|
||||
if(item.CustomFormatterTypeName != null) {
|
||||
this.Write(" ");
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(item.CustomFormatterTypeName));
|
||||
this.Write(" __");
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(item.Name));
|
||||
this.Write("CustomFormatter__ = new ");
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(item.CustomFormatterTypeName));
|
||||
this.Write("();\r\n");
|
||||
}
|
||||
}
|
||||
this.Write("\r\n");
|
||||
if( objInfo.IsStringKey) {
|
||||
this.Write("\r\n private readonly global::MessagePack.Internal.AutomataDictionary ____ke" +
|
||||
"yMapping;\r\n private readonly byte[][] ____stringByteKeys;\r\n\r\n publ" +
|
||||
"ic ");
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(objInfo.Name));
|
||||
this.Write("Formatter()\r\n {\r\n this.____keyMapping = new global::MessagePack" +
|
||||
".Internal.AutomataDictionary()\r\n {\r\n");
|
||||
foreach(var x in objInfo.Members) {
|
||||
this.Write(" { \"");
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(x.StringKey));
|
||||
this.Write("\", ");
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(x.IntKey));
|
||||
this.Write(" },\r\n");
|
||||
}
|
||||
this.Write(" };\r\n\r\n this.____stringByteKeys = new byte[][]\r\n " +
|
||||
" {\r\n");
|
||||
foreach(var x in objInfo.Members.Where(x => x.IsReadable)) {
|
||||
this.Write(" global::MessagePack.Internal.CodeGenHelpers.GetEncodedStringBytes" +
|
||||
"(\"");
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(x.StringKey));
|
||||
this.Write("\"),\r\n");
|
||||
}
|
||||
this.Write(" };\r\n }\r\n");
|
||||
}
|
||||
this.Write("\r\n public void Serialize(ref MessagePackWriter writer, ");
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(objInfo.FullName));
|
||||
this.Write(" value, global::MessagePack.MessagePackSerializerOptions options)\r\n {\r\n");
|
||||
if( objInfo.IsClass) {
|
||||
this.Write(" if (value == null)\r\n {\r\n writer.WriteNil();" +
|
||||
"\r\n return;\r\n }\r\n\r\n");
|
||||
}
|
||||
this.Write(" IFormatterResolver formatterResolver = options.Resolver;\r\n");
|
||||
if(objInfo.HasIMessagePackSerializationCallbackReceiver && objInfo.NeedsCastOnBefore) {
|
||||
this.Write(" ((IMessagePackSerializationCallbackReceiver)value).OnBeforeSerialize(" +
|
||||
");\r\n");
|
||||
} else if(objInfo.HasIMessagePackSerializationCallbackReceiver) {
|
||||
this.Write(" value.OnBeforeSerialize();\r\n");
|
||||
}
|
||||
if( objInfo.IsIntKey) {
|
||||
this.Write(" writer.WriteArrayHeader(");
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(objInfo.MaxKey + 1));
|
||||
this.Write(");\r\n");
|
||||
} else {
|
||||
this.Write(" writer.WriteMapHeader(");
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(objInfo.WriteCount));
|
||||
this.Write(");\r\n");
|
||||
}
|
||||
if(objInfo.IsIntKey) {
|
||||
for(var i =0; i<= objInfo.MaxKey; i++) { var member = objInfo.GetMember(i);
|
||||
if( member == null) {
|
||||
this.Write(" writer.WriteNil();\r\n");
|
||||
} else {
|
||||
this.Write(" ");
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(member.GetSerializeMethodString()));
|
||||
this.Write(";\r\n");
|
||||
} } } else {
|
||||
var index = 0; foreach(var x in objInfo.Members) {
|
||||
this.Write(" writer.WriteRaw(this.____stringByteKeys[");
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(index++));
|
||||
this.Write("]);\r\n ");
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(x.GetSerializeMethodString()));
|
||||
this.Write(";\r\n");
|
||||
} }
|
||||
this.Write(" }\r\n\r\n public ");
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(objInfo.FullName));
|
||||
this.Write(" Deserialize(ref MessagePackReader reader, global::MessagePack.MessagePackSeriali" +
|
||||
"zerOptions options)\r\n {\r\n if (reader.TryReadNil())\r\n " +
|
||||
" {\r\n");
|
||||
if( objInfo.IsClass) {
|
||||
this.Write(" return null;\r\n");
|
||||
} else {
|
||||
this.Write(" throw new InvalidOperationException(\"typecode is null, struct not" +
|
||||
" supported\");\r\n");
|
||||
}
|
||||
this.Write(" }\r\n\r\n IFormatterResolver formatterResolver = options.Resol" +
|
||||
"ver;\r\n");
|
||||
if(objInfo.IsStringKey) {
|
||||
this.Write(" var length = reader.ReadMapHeader();\r\n");
|
||||
} else {
|
||||
this.Write(" var length = reader.ReadArrayHeader();\r\n");
|
||||
}
|
||||
foreach(var x in objInfo.Members) {
|
||||
this.Write(" var __");
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(x.Name));
|
||||
this.Write("__ = default(");
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(x.Type));
|
||||
this.Write(");\r\n");
|
||||
}
|
||||
this.Write("\r\n for (int i = 0; i < length; i++)\r\n {\r\n");
|
||||
if(objInfo.IsStringKey) {
|
||||
this.Write(@" ReadOnlySequence<byte> stringKey = reader.ReadStringSegment().Value;
|
||||
int key;
|
||||
if (!this.____keyMapping.TryGetValue(stringKey, out key))
|
||||
{
|
||||
reader.Skip();
|
||||
continue;
|
||||
}
|
||||
");
|
||||
} else {
|
||||
this.Write(" var key = i;\r\n");
|
||||
}
|
||||
this.Write("\r\n switch (key)\r\n {\r\n");
|
||||
foreach(var x in objInfo.Members) {
|
||||
this.Write(" case ");
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(x.IntKey));
|
||||
this.Write(":\r\n __");
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(x.Name));
|
||||
this.Write("__ = ");
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(x.GetDeserializeMethodString()));
|
||||
this.Write(";\r\n break;\r\n");
|
||||
}
|
||||
this.Write(" default:\r\n reader.Skip();\r\n " +
|
||||
" break;\r\n }\r\n }\r\n\r\n var ____res" +
|
||||
"ult = new ");
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(objInfo.GetConstructorString()));
|
||||
this.Write(";\r\n");
|
||||
foreach(var x in objInfo.Members.Where(x => x.IsWritable)) {
|
||||
this.Write(" ____result.");
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(x.Name));
|
||||
this.Write(" = __");
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(x.Name));
|
||||
this.Write("__;\r\n");
|
||||
}
|
||||
if(objInfo.HasIMessagePackSerializationCallbackReceiver && objInfo.NeedsCastOnAfter) {
|
||||
this.Write(" ((IMessagePackSerializationCallbackReceiver)____result).OnAfterDeseri" +
|
||||
"alize();\r\n");
|
||||
} else if(objInfo.HasIMessagePackSerializationCallbackReceiver) {
|
||||
this.Write(" ____result.OnAfterDeserialize();\r\n");
|
||||
}
|
||||
this.Write(" return ____result;\r\n }\r\n }\r\n");
|
||||
}
|
||||
this.Write(@"}
|
||||
|
||||
#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 SA1200 // Using directives should be placed correctly
|
||||
#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
|
||||
");
|
||||
return this.GenerationEnvironment.ToString();
|
||||
}
|
||||
}
|
||||
#region Base class
|
||||
/// <summary>
|
||||
/// Base class for this transformation
|
||||
/// </summary>
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "16.0.0.0")]
|
||||
public class FormatterTemplateBase
|
||||
{
|
||||
#region Fields
|
||||
private global::System.Text.StringBuilder generationEnvironmentField;
|
||||
private global::System.CodeDom.Compiler.CompilerErrorCollection errorsField;
|
||||
private global::System.Collections.Generic.List<int> indentLengthsField;
|
||||
private string currentIndentField = "";
|
||||
private bool endsWithNewline;
|
||||
private global::System.Collections.Generic.IDictionary<string, object> sessionField;
|
||||
#endregion
|
||||
#region Properties
|
||||
/// <summary>
|
||||
/// The string builder that generation-time code is using to assemble generated output
|
||||
/// </summary>
|
||||
protected System.Text.StringBuilder GenerationEnvironment
|
||||
{
|
||||
get
|
||||
{
|
||||
if ((this.generationEnvironmentField == null))
|
||||
{
|
||||
this.generationEnvironmentField = new global::System.Text.StringBuilder();
|
||||
}
|
||||
return this.generationEnvironmentField;
|
||||
}
|
||||
set
|
||||
{
|
||||
this.generationEnvironmentField = value;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// The error collection for the generation process
|
||||
/// </summary>
|
||||
public System.CodeDom.Compiler.CompilerErrorCollection Errors
|
||||
{
|
||||
get
|
||||
{
|
||||
if ((this.errorsField == null))
|
||||
{
|
||||
this.errorsField = new global::System.CodeDom.Compiler.CompilerErrorCollection();
|
||||
}
|
||||
return this.errorsField;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// A list of the lengths of each indent that was added with PushIndent
|
||||
/// </summary>
|
||||
private System.Collections.Generic.List<int> indentLengths
|
||||
{
|
||||
get
|
||||
{
|
||||
if ((this.indentLengthsField == null))
|
||||
{
|
||||
this.indentLengthsField = new global::System.Collections.Generic.List<int>();
|
||||
}
|
||||
return this.indentLengthsField;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets the current indent we use when adding lines to the output
|
||||
/// </summary>
|
||||
public string CurrentIndent
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.currentIndentField;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Current transformation session
|
||||
/// </summary>
|
||||
public virtual global::System.Collections.Generic.IDictionary<string, object> Session
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.sessionField;
|
||||
}
|
||||
set
|
||||
{
|
||||
this.sessionField = value;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
#region Transform-time helpers
|
||||
/// <summary>
|
||||
/// Write text directly into the generated output
|
||||
/// </summary>
|
||||
public void Write(string textToAppend)
|
||||
{
|
||||
if (string.IsNullOrEmpty(textToAppend))
|
||||
{
|
||||
return;
|
||||
}
|
||||
// If we're starting off, or if the previous text ended with a newline,
|
||||
// we have to append the current indent first.
|
||||
if (((this.GenerationEnvironment.Length == 0)
|
||||
|| this.endsWithNewline))
|
||||
{
|
||||
this.GenerationEnvironment.Append(this.currentIndentField);
|
||||
this.endsWithNewline = false;
|
||||
}
|
||||
// Check if the current text ends with a newline
|
||||
if (textToAppend.EndsWith(global::System.Environment.NewLine, global::System.StringComparison.CurrentCulture))
|
||||
{
|
||||
this.endsWithNewline = true;
|
||||
}
|
||||
// This is an optimization. If the current indent is "", then we don't have to do any
|
||||
// of the more complex stuff further down.
|
||||
if ((this.currentIndentField.Length == 0))
|
||||
{
|
||||
this.GenerationEnvironment.Append(textToAppend);
|
||||
return;
|
||||
}
|
||||
// Everywhere there is a newline in the text, add an indent after it
|
||||
textToAppend = textToAppend.Replace(global::System.Environment.NewLine, (global::System.Environment.NewLine + this.currentIndentField));
|
||||
// If the text ends with a newline, then we should strip off the indent added at the very end
|
||||
// because the appropriate indent will be added when the next time Write() is called
|
||||
if (this.endsWithNewline)
|
||||
{
|
||||
this.GenerationEnvironment.Append(textToAppend, 0, (textToAppend.Length - this.currentIndentField.Length));
|
||||
}
|
||||
else
|
||||
{
|
||||
this.GenerationEnvironment.Append(textToAppend);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Write text directly into the generated output
|
||||
/// </summary>
|
||||
public void WriteLine(string textToAppend)
|
||||
{
|
||||
this.Write(textToAppend);
|
||||
this.GenerationEnvironment.AppendLine();
|
||||
this.endsWithNewline = true;
|
||||
}
|
||||
/// <summary>
|
||||
/// Write formatted text directly into the generated output
|
||||
/// </summary>
|
||||
public void Write(string format, params object[] args)
|
||||
{
|
||||
this.Write(string.Format(global::System.Globalization.CultureInfo.CurrentCulture, format, args));
|
||||
}
|
||||
/// <summary>
|
||||
/// Write formatted text directly into the generated output
|
||||
/// </summary>
|
||||
public void WriteLine(string format, params object[] args)
|
||||
{
|
||||
this.WriteLine(string.Format(global::System.Globalization.CultureInfo.CurrentCulture, format, args));
|
||||
}
|
||||
/// <summary>
|
||||
/// Raise an error
|
||||
/// </summary>
|
||||
public void Error(string message)
|
||||
{
|
||||
System.CodeDom.Compiler.CompilerError error = new global::System.CodeDom.Compiler.CompilerError();
|
||||
error.ErrorText = message;
|
||||
this.Errors.Add(error);
|
||||
}
|
||||
/// <summary>
|
||||
/// Raise a warning
|
||||
/// </summary>
|
||||
public void Warning(string message)
|
||||
{
|
||||
System.CodeDom.Compiler.CompilerError error = new global::System.CodeDom.Compiler.CompilerError();
|
||||
error.ErrorText = message;
|
||||
error.IsWarning = true;
|
||||
this.Errors.Add(error);
|
||||
}
|
||||
/// <summary>
|
||||
/// Increase the indent
|
||||
/// </summary>
|
||||
public void PushIndent(string indent)
|
||||
{
|
||||
if ((indent == null))
|
||||
{
|
||||
throw new global::System.ArgumentNullException("indent");
|
||||
}
|
||||
this.currentIndentField = (this.currentIndentField + indent);
|
||||
this.indentLengths.Add(indent.Length);
|
||||
}
|
||||
/// <summary>
|
||||
/// Remove the last indent that was added with PushIndent
|
||||
/// </summary>
|
||||
public string PopIndent()
|
||||
{
|
||||
string returnValue = "";
|
||||
if ((this.indentLengths.Count > 0))
|
||||
{
|
||||
int indentLength = this.indentLengths[(this.indentLengths.Count - 1)];
|
||||
this.indentLengths.RemoveAt((this.indentLengths.Count - 1));
|
||||
if ((indentLength > 0))
|
||||
{
|
||||
returnValue = this.currentIndentField.Substring((this.currentIndentField.Length - indentLength));
|
||||
this.currentIndentField = this.currentIndentField.Remove((this.currentIndentField.Length - indentLength));
|
||||
}
|
||||
}
|
||||
return returnValue;
|
||||
}
|
||||
/// <summary>
|
||||
/// Remove any indentation
|
||||
/// </summary>
|
||||
public void ClearIndent()
|
||||
{
|
||||
this.indentLengths.Clear();
|
||||
this.currentIndentField = "";
|
||||
}
|
||||
#endregion
|
||||
#region ToString Helpers
|
||||
/// <summary>
|
||||
/// Utility class to produce culture-oriented representation of an object as a string.
|
||||
/// </summary>
|
||||
public class ToStringInstanceHelper
|
||||
{
|
||||
private System.IFormatProvider formatProviderField = global::System.Globalization.CultureInfo.InvariantCulture;
|
||||
/// <summary>
|
||||
/// Gets or sets format provider to be used by ToStringWithCulture method.
|
||||
/// </summary>
|
||||
public System.IFormatProvider FormatProvider
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.formatProviderField ;
|
||||
}
|
||||
set
|
||||
{
|
||||
if ((value != null))
|
||||
{
|
||||
this.formatProviderField = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// This is called from the compile/run appdomain to convert objects within an expression block to a string
|
||||
/// </summary>
|
||||
public string ToStringWithCulture(object objectToConvert)
|
||||
{
|
||||
if ((objectToConvert == null))
|
||||
{
|
||||
throw new global::System.ArgumentNullException("objectToConvert");
|
||||
}
|
||||
System.Type t = objectToConvert.GetType();
|
||||
System.Reflection.MethodInfo method = t.GetMethod("ToString", new System.Type[] {
|
||||
typeof(System.IFormatProvider)});
|
||||
if ((method == null))
|
||||
{
|
||||
return objectToConvert.ToString();
|
||||
}
|
||||
else
|
||||
{
|
||||
return ((string)(method.Invoke(objectToConvert, new object[] {
|
||||
this.formatProviderField })));
|
||||
}
|
||||
}
|
||||
}
|
||||
private ToStringInstanceHelper toStringHelperField = new ToStringInstanceHelper();
|
||||
/// <summary>
|
||||
/// Helper to produce culture-oriented representation of an object as a string
|
||||
/// </summary>
|
||||
public ToStringInstanceHelper ToStringHelper
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.toStringHelperField;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
#endregion
|
||||
}
|
|
@ -0,0 +1,163 @@
|
|||
<#@ template debug="false" hostspecific="false" linePragmas="false" language="C#" #>
|
||||
<#@ assembly name="System.Core" #>
|
||||
<#@ import namespace="System.Linq" #>
|
||||
<#@ import namespace="System.Text" #>
|
||||
<#@ import namespace="System.Collections.Generic" #>
|
||||
#pragma warning disable 618
|
||||
#pragma warning disable 612
|
||||
#pragma warning disable 414
|
||||
#pragma warning disable 168
|
||||
|
||||
#pragma warning disable SA1129 // Do not use default value type constructor
|
||||
#pragma warning disable SA1200 // Using directives should be placed correctly
|
||||
#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 <#= Namespace #>
|
||||
{
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using MessagePack;
|
||||
<# foreach(var objInfo in ObjectSerializationInfos) { #>
|
||||
|
||||
public sealed class <#= objInfo.Name #>Formatter : global::MessagePack.Formatters.IMessagePackFormatter<<#= objInfo.FullName #>>
|
||||
{
|
||||
<# foreach(var item in objInfo.Members) { #>
|
||||
<# if(item.CustomFormatterTypeName != null) { #>
|
||||
<#= item.CustomFormatterTypeName #> __<#= item.Name #>CustomFormatter__ = new <#= item.CustomFormatterTypeName #>();
|
||||
<# } #>
|
||||
<# } #>
|
||||
|
||||
<# if( objInfo.IsStringKey) { #>
|
||||
|
||||
private readonly global::MessagePack.Internal.AutomataDictionary ____keyMapping;
|
||||
private readonly byte[][] ____stringByteKeys;
|
||||
|
||||
public <#= objInfo.Name #>Formatter()
|
||||
{
|
||||
this.____keyMapping = new global::MessagePack.Internal.AutomataDictionary()
|
||||
{
|
||||
<# foreach(var x in objInfo.Members) { #>
|
||||
{ "<#= x.StringKey #>", <#= x.IntKey #> },
|
||||
<# } #>
|
||||
};
|
||||
|
||||
this.____stringByteKeys = new byte[][]
|
||||
{
|
||||
<# foreach(var x in objInfo.Members.Where(x => x.IsReadable)) { #>
|
||||
global::MessagePack.Internal.CodeGenHelpers.GetEncodedStringBytes("<#= x.StringKey #>"),
|
||||
<# } #>
|
||||
};
|
||||
}
|
||||
<# } #>
|
||||
|
||||
public void Serialize(ref MessagePackWriter writer, <#= objInfo.FullName #> value, global::MessagePack.MessagePackSerializerOptions options)
|
||||
{
|
||||
<# if( objInfo.IsClass) { #>
|
||||
if (value == null)
|
||||
{
|
||||
writer.WriteNil();
|
||||
return;
|
||||
}
|
||||
|
||||
<# } #>
|
||||
IFormatterResolver formatterResolver = options.Resolver;
|
||||
<#if(objInfo.HasIMessagePackSerializationCallbackReceiver && objInfo.NeedsCastOnBefore) { #>
|
||||
((IMessagePackSerializationCallbackReceiver)value).OnBeforeSerialize();
|
||||
<# } else if(objInfo.HasIMessagePackSerializationCallbackReceiver) { #>
|
||||
value.OnBeforeSerialize();
|
||||
<# } #>
|
||||
<# if( objInfo.IsIntKey) { #>
|
||||
writer.WriteArrayHeader(<#= objInfo.MaxKey + 1 #>);
|
||||
<# } else { #>
|
||||
writer.WriteMapHeader(<#= objInfo.WriteCount #>);
|
||||
<# } #>
|
||||
<# if(objInfo.IsIntKey) { #>
|
||||
<# for(var i =0; i<= objInfo.MaxKey; i++) { var member = objInfo.GetMember(i); #>
|
||||
<# if( member == null) { #>
|
||||
writer.WriteNil();
|
||||
<# } else { #>
|
||||
<#= member.GetSerializeMethodString() #>;
|
||||
<# } } } else { #>
|
||||
<# var index = 0; foreach(var x in objInfo.Members) { #>
|
||||
writer.WriteRaw(this.____stringByteKeys[<#= index++ #>]);
|
||||
<#= x.GetSerializeMethodString() #>;
|
||||
<# } } #>
|
||||
}
|
||||
|
||||
public <#= objInfo.FullName #> Deserialize(ref MessagePackReader reader, global::MessagePack.MessagePackSerializerOptions options)
|
||||
{
|
||||
if (reader.TryReadNil())
|
||||
{
|
||||
<# if( objInfo.IsClass) { #>
|
||||
return null;
|
||||
<# } else { #>
|
||||
throw new InvalidOperationException("typecode is null, struct not supported");
|
||||
<# } #>
|
||||
}
|
||||
|
||||
IFormatterResolver formatterResolver = options.Resolver;
|
||||
<# if(objInfo.IsStringKey) { #>
|
||||
var length = reader.ReadMapHeader();
|
||||
<# } else { #>
|
||||
var length = reader.ReadArrayHeader();
|
||||
<# } #>
|
||||
<# foreach(var x in objInfo.Members) { #>
|
||||
var __<#= x.Name #>__ = default(<#= x.Type #>);
|
||||
<# } #>
|
||||
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
<# if(objInfo.IsStringKey) { #>
|
||||
ReadOnlySequence<byte> stringKey = reader.ReadStringSegment().Value;
|
||||
int key;
|
||||
if (!this.____keyMapping.TryGetValue(stringKey, out key))
|
||||
{
|
||||
reader.Skip();
|
||||
continue;
|
||||
}
|
||||
<# } else { #>
|
||||
var key = i;
|
||||
<# } #>
|
||||
|
||||
switch (key)
|
||||
{
|
||||
<# foreach(var x in objInfo.Members) { #>
|
||||
case <#= x.IntKey #>:
|
||||
__<#= x.Name #>__ = <#= x.GetDeserializeMethodString() #>;
|
||||
break;
|
||||
<# } #>
|
||||
default:
|
||||
reader.Skip();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var ____result = new <#= objInfo.GetConstructorString() #>;
|
||||
<# foreach(var x in objInfo.Members.Where(x => x.IsWritable)) { #>
|
||||
____result.<#= x.Name #> = __<#= x.Name #>__;
|
||||
<# } #>
|
||||
<#if(objInfo.HasIMessagePackSerializationCallbackReceiver && objInfo.NeedsCastOnAfter) { #>
|
||||
((IMessagePackSerializationCallbackReceiver)____result).OnAfterDeserialize();
|
||||
<# } else if(objInfo.HasIMessagePackSerializationCallbackReceiver) { #>
|
||||
____result.OnAfterDeserialize();
|
||||
<# } #>
|
||||
return ____result;
|
||||
}
|
||||
}
|
||||
<# } #>
|
||||
}
|
||||
|
||||
#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 SA1200 // Using directives should be placed correctly
|
||||
#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
|
|
@ -0,0 +1,400 @@
|
|||
// ------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// このコードはツールによって生成されました。
|
||||
// ランタイム バージョン: 16.0.0.0
|
||||
//
|
||||
// このファイルへの変更は、正しくない動作の原因になる可能性があり、
|
||||
// コードが再生成されると失われます。
|
||||
// </auto-generated>
|
||||
// ------------------------------------------------------------------------------
|
||||
namespace MessagePackCompiler.Generator
|
||||
{
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
|
||||
/// <summary>
|
||||
/// Class to produce the template output
|
||||
/// </summary>
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "16.0.0.0")]
|
||||
public partial class ResolverTemplate : ResolverTemplateBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Create the template output
|
||||
/// </summary>
|
||||
public virtual string TransformText()
|
||||
{
|
||||
this.Write(@"#pragma warning disable 618
|
||||
#pragma warning disable 612
|
||||
#pragma warning disable 414
|
||||
#pragma warning disable 168
|
||||
|
||||
#pragma warning disable SA1200 // Using directives should be placed correctly
|
||||
#pragma warning disable SA1312 // Variable names should begin with lower-case letter
|
||||
#pragma warning disable SA1649 // File name should match first type name
|
||||
|
||||
namespace ");
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(Namespace));
|
||||
this.Write("\r\n{\r\n using System;\r\n using System.Buffers;\r\n using MessagePack;\r\n\r\n " +
|
||||
"public class ");
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(ResolverName));
|
||||
this.Write(" : global::MessagePack.IFormatterResolver\r\n {\r\n public static readonly " +
|
||||
"global::MessagePack.IFormatterResolver Instance = new ");
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(ResolverName));
|
||||
this.Write("();\r\n\r\n private ");
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(ResolverName));
|
||||
this.Write(@"()
|
||||
{
|
||||
}
|
||||
|
||||
public global::MessagePack.Formatters.IMessagePackFormatter<T> GetFormatter<T>()
|
||||
{
|
||||
return FormatterCache<T>.Formatter;
|
||||
}
|
||||
|
||||
private static class FormatterCache<T>
|
||||
{
|
||||
internal static readonly global::MessagePack.Formatters.IMessagePackFormatter<T> Formatter;
|
||||
|
||||
static FormatterCache()
|
||||
{
|
||||
var f = ");
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(ResolverName));
|
||||
this.Write(@"GetFormatterHelper.GetFormatter(typeof(T));
|
||||
if (f != null)
|
||||
{
|
||||
Formatter = (global::MessagePack.Formatters.IMessagePackFormatter<T>)f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static class ");
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(ResolverName));
|
||||
this.Write("GetFormatterHelper\r\n {\r\n private static readonly global::System.Collect" +
|
||||
"ions.Generic.Dictionary<Type, int> lookup;\r\n\r\n static ");
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(ResolverName));
|
||||
this.Write("GetFormatterHelper()\r\n {\r\n lookup = new global::System.Collecti" +
|
||||
"ons.Generic.Dictionary<Type, int>(");
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(RegisterInfos.Length));
|
||||
this.Write(")\r\n {\r\n");
|
||||
for(var i = 0; i < RegisterInfos.Length; i++) { var x = RegisterInfos[i];
|
||||
this.Write(" { typeof(");
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(x.FullName));
|
||||
this.Write("), ");
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(i));
|
||||
this.Write(" },\r\n");
|
||||
}
|
||||
this.Write(@" };
|
||||
}
|
||||
|
||||
internal static object GetFormatter(Type t)
|
||||
{
|
||||
int key;
|
||||
if (!lookup.TryGetValue(t, out key))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
switch (key)
|
||||
{
|
||||
");
|
||||
for(var i = 0; i < RegisterInfos.Length; i++) { var x = RegisterInfos[i];
|
||||
this.Write(" case ");
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(i));
|
||||
this.Write(": return new ");
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(x.FormatterName.StartsWith("global::") ? x.FormatterName: (!string.IsNullOrEmpty(FormatterNamespace) ? FormatterNamespace + "." : FormatterNamespace) + x.FormatterName));
|
||||
this.Write("();\r\n");
|
||||
}
|
||||
this.Write(@" default: return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma warning restore 168
|
||||
#pragma warning restore 414
|
||||
#pragma warning restore 618
|
||||
#pragma warning restore 612
|
||||
|
||||
#pragma warning restore SA1312 // Variable names should begin with lower-case letter
|
||||
#pragma warning restore SA1200 // Using directives should be placed correctly
|
||||
#pragma warning restore SA1649 // File name should match first type name
|
||||
");
|
||||
return this.GenerationEnvironment.ToString();
|
||||
}
|
||||
}
|
||||
#region Base class
|
||||
/// <summary>
|
||||
/// Base class for this transformation
|
||||
/// </summary>
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "16.0.0.0")]
|
||||
public class ResolverTemplateBase
|
||||
{
|
||||
#region Fields
|
||||
private global::System.Text.StringBuilder generationEnvironmentField;
|
||||
private global::System.CodeDom.Compiler.CompilerErrorCollection errorsField;
|
||||
private global::System.Collections.Generic.List<int> indentLengthsField;
|
||||
private string currentIndentField = "";
|
||||
private bool endsWithNewline;
|
||||
private global::System.Collections.Generic.IDictionary<string, object> sessionField;
|
||||
#endregion
|
||||
#region Properties
|
||||
/// <summary>
|
||||
/// The string builder that generation-time code is using to assemble generated output
|
||||
/// </summary>
|
||||
protected System.Text.StringBuilder GenerationEnvironment
|
||||
{
|
||||
get
|
||||
{
|
||||
if ((this.generationEnvironmentField == null))
|
||||
{
|
||||
this.generationEnvironmentField = new global::System.Text.StringBuilder();
|
||||
}
|
||||
return this.generationEnvironmentField;
|
||||
}
|
||||
set
|
||||
{
|
||||
this.generationEnvironmentField = value;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// The error collection for the generation process
|
||||
/// </summary>
|
||||
public System.CodeDom.Compiler.CompilerErrorCollection Errors
|
||||
{
|
||||
get
|
||||
{
|
||||
if ((this.errorsField == null))
|
||||
{
|
||||
this.errorsField = new global::System.CodeDom.Compiler.CompilerErrorCollection();
|
||||
}
|
||||
return this.errorsField;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// A list of the lengths of each indent that was added with PushIndent
|
||||
/// </summary>
|
||||
private System.Collections.Generic.List<int> indentLengths
|
||||
{
|
||||
get
|
||||
{
|
||||
if ((this.indentLengthsField == null))
|
||||
{
|
||||
this.indentLengthsField = new global::System.Collections.Generic.List<int>();
|
||||
}
|
||||
return this.indentLengthsField;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets the current indent we use when adding lines to the output
|
||||
/// </summary>
|
||||
public string CurrentIndent
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.currentIndentField;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Current transformation session
|
||||
/// </summary>
|
||||
public virtual global::System.Collections.Generic.IDictionary<string, object> Session
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.sessionField;
|
||||
}
|
||||
set
|
||||
{
|
||||
this.sessionField = value;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
#region Transform-time helpers
|
||||
/// <summary>
|
||||
/// Write text directly into the generated output
|
||||
/// </summary>
|
||||
public void Write(string textToAppend)
|
||||
{
|
||||
if (string.IsNullOrEmpty(textToAppend))
|
||||
{
|
||||
return;
|
||||
}
|
||||
// If we're starting off, or if the previous text ended with a newline,
|
||||
// we have to append the current indent first.
|
||||
if (((this.GenerationEnvironment.Length == 0)
|
||||
|| this.endsWithNewline))
|
||||
{
|
||||
this.GenerationEnvironment.Append(this.currentIndentField);
|
||||
this.endsWithNewline = false;
|
||||
}
|
||||
// Check if the current text ends with a newline
|
||||
if (textToAppend.EndsWith(global::System.Environment.NewLine, global::System.StringComparison.CurrentCulture))
|
||||
{
|
||||
this.endsWithNewline = true;
|
||||
}
|
||||
// This is an optimization. If the current indent is "", then we don't have to do any
|
||||
// of the more complex stuff further down.
|
||||
if ((this.currentIndentField.Length == 0))
|
||||
{
|
||||
this.GenerationEnvironment.Append(textToAppend);
|
||||
return;
|
||||
}
|
||||
// Everywhere there is a newline in the text, add an indent after it
|
||||
textToAppend = textToAppend.Replace(global::System.Environment.NewLine, (global::System.Environment.NewLine + this.currentIndentField));
|
||||
// If the text ends with a newline, then we should strip off the indent added at the very end
|
||||
// because the appropriate indent will be added when the next time Write() is called
|
||||
if (this.endsWithNewline)
|
||||
{
|
||||
this.GenerationEnvironment.Append(textToAppend, 0, (textToAppend.Length - this.currentIndentField.Length));
|
||||
}
|
||||
else
|
||||
{
|
||||
this.GenerationEnvironment.Append(textToAppend);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Write text directly into the generated output
|
||||
/// </summary>
|
||||
public void WriteLine(string textToAppend)
|
||||
{
|
||||
this.Write(textToAppend);
|
||||
this.GenerationEnvironment.AppendLine();
|
||||
this.endsWithNewline = true;
|
||||
}
|
||||
/// <summary>
|
||||
/// Write formatted text directly into the generated output
|
||||
/// </summary>
|
||||
public void Write(string format, params object[] args)
|
||||
{
|
||||
this.Write(string.Format(global::System.Globalization.CultureInfo.CurrentCulture, format, args));
|
||||
}
|
||||
/// <summary>
|
||||
/// Write formatted text directly into the generated output
|
||||
/// </summary>
|
||||
public void WriteLine(string format, params object[] args)
|
||||
{
|
||||
this.WriteLine(string.Format(global::System.Globalization.CultureInfo.CurrentCulture, format, args));
|
||||
}
|
||||
/// <summary>
|
||||
/// Raise an error
|
||||
/// </summary>
|
||||
public void Error(string message)
|
||||
{
|
||||
System.CodeDom.Compiler.CompilerError error = new global::System.CodeDom.Compiler.CompilerError();
|
||||
error.ErrorText = message;
|
||||
this.Errors.Add(error);
|
||||
}
|
||||
/// <summary>
|
||||
/// Raise a warning
|
||||
/// </summary>
|
||||
public void Warning(string message)
|
||||
{
|
||||
System.CodeDom.Compiler.CompilerError error = new global::System.CodeDom.Compiler.CompilerError();
|
||||
error.ErrorText = message;
|
||||
error.IsWarning = true;
|
||||
this.Errors.Add(error);
|
||||
}
|
||||
/// <summary>
|
||||
/// Increase the indent
|
||||
/// </summary>
|
||||
public void PushIndent(string indent)
|
||||
{
|
||||
if ((indent == null))
|
||||
{
|
||||
throw new global::System.ArgumentNullException("indent");
|
||||
}
|
||||
this.currentIndentField = (this.currentIndentField + indent);
|
||||
this.indentLengths.Add(indent.Length);
|
||||
}
|
||||
/// <summary>
|
||||
/// Remove the last indent that was added with PushIndent
|
||||
/// </summary>
|
||||
public string PopIndent()
|
||||
{
|
||||
string returnValue = "";
|
||||
if ((this.indentLengths.Count > 0))
|
||||
{
|
||||
int indentLength = this.indentLengths[(this.indentLengths.Count - 1)];
|
||||
this.indentLengths.RemoveAt((this.indentLengths.Count - 1));
|
||||
if ((indentLength > 0))
|
||||
{
|
||||
returnValue = this.currentIndentField.Substring((this.currentIndentField.Length - indentLength));
|
||||
this.currentIndentField = this.currentIndentField.Remove((this.currentIndentField.Length - indentLength));
|
||||
}
|
||||
}
|
||||
return returnValue;
|
||||
}
|
||||
/// <summary>
|
||||
/// Remove any indentation
|
||||
/// </summary>
|
||||
public void ClearIndent()
|
||||
{
|
||||
this.indentLengths.Clear();
|
||||
this.currentIndentField = "";
|
||||
}
|
||||
#endregion
|
||||
#region ToString Helpers
|
||||
/// <summary>
|
||||
/// Utility class to produce culture-oriented representation of an object as a string.
|
||||
/// </summary>
|
||||
public class ToStringInstanceHelper
|
||||
{
|
||||
private System.IFormatProvider formatProviderField = global::System.Globalization.CultureInfo.InvariantCulture;
|
||||
/// <summary>
|
||||
/// Gets or sets format provider to be used by ToStringWithCulture method.
|
||||
/// </summary>
|
||||
public System.IFormatProvider FormatProvider
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.formatProviderField ;
|
||||
}
|
||||
set
|
||||
{
|
||||
if ((value != null))
|
||||
{
|
||||
this.formatProviderField = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// This is called from the compile/run appdomain to convert objects within an expression block to a string
|
||||
/// </summary>
|
||||
public string ToStringWithCulture(object objectToConvert)
|
||||
{
|
||||
if ((objectToConvert == null))
|
||||
{
|
||||
throw new global::System.ArgumentNullException("objectToConvert");
|
||||
}
|
||||
System.Type t = objectToConvert.GetType();
|
||||
System.Reflection.MethodInfo method = t.GetMethod("ToString", new System.Type[] {
|
||||
typeof(System.IFormatProvider)});
|
||||
if ((method == null))
|
||||
{
|
||||
return objectToConvert.ToString();
|
||||
}
|
||||
else
|
||||
{
|
||||
return ((string)(method.Invoke(objectToConvert, new object[] {
|
||||
this.formatProviderField })));
|
||||
}
|
||||
}
|
||||
}
|
||||
private ToStringInstanceHelper toStringHelperField = new ToStringInstanceHelper();
|
||||
/// <summary>
|
||||
/// Helper to produce culture-oriented representation of an object as a string
|
||||
/// </summary>
|
||||
public ToStringInstanceHelper ToStringHelper
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.toStringHelperField;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
#endregion
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
<#@ template debug="false" hostspecific="false" linePragmas="false" language="C#" #>
|
||||
<#@ assembly name="System.Core" #>
|
||||
<#@ import namespace="System.Linq" #>
|
||||
<#@ import namespace="System.Text" #>
|
||||
<#@ import namespace="System.Collections.Generic" #>
|
||||
#pragma warning disable 618
|
||||
#pragma warning disable 612
|
||||
#pragma warning disable 414
|
||||
#pragma warning disable 168
|
||||
|
||||
#pragma warning disable SA1200 // Using directives should be placed correctly
|
||||
#pragma warning disable SA1312 // Variable names should begin with lower-case letter
|
||||
#pragma warning disable SA1649 // File name should match first type name
|
||||
|
||||
namespace <#= Namespace #>
|
||||
{
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using MessagePack;
|
||||
|
||||
public class <#= ResolverName #> : global::MessagePack.IFormatterResolver
|
||||
{
|
||||
public static readonly global::MessagePack.IFormatterResolver Instance = new <#= ResolverName #>();
|
||||
|
||||
private <#= ResolverName #>()
|
||||
{
|
||||
}
|
||||
|
||||
public global::MessagePack.Formatters.IMessagePackFormatter<T> GetFormatter<T>()
|
||||
{
|
||||
return FormatterCache<T>.Formatter;
|
||||
}
|
||||
|
||||
private static class FormatterCache<T>
|
||||
{
|
||||
internal static readonly global::MessagePack.Formatters.IMessagePackFormatter<T> Formatter;
|
||||
|
||||
static FormatterCache()
|
||||
{
|
||||
var f = <#= ResolverName #>GetFormatterHelper.GetFormatter(typeof(T));
|
||||
if (f != null)
|
||||
{
|
||||
Formatter = (global::MessagePack.Formatters.IMessagePackFormatter<T>)f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static class <#= ResolverName #>GetFormatterHelper
|
||||
{
|
||||
private static readonly global::System.Collections.Generic.Dictionary<Type, int> lookup;
|
||||
|
||||
static <#= ResolverName #>GetFormatterHelper()
|
||||
{
|
||||
lookup = new global::System.Collections.Generic.Dictionary<Type, int>(<#= RegisterInfos.Length #>)
|
||||
{
|
||||
<# for(var i = 0; i < RegisterInfos.Length; i++) { var x = RegisterInfos[i]; #>
|
||||
{ typeof(<#= x.FullName #>), <#= i #> },
|
||||
<# } #>
|
||||
};
|
||||
}
|
||||
|
||||
internal static object GetFormatter(Type t)
|
||||
{
|
||||
int key;
|
||||
if (!lookup.TryGetValue(t, out key))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
switch (key)
|
||||
{
|
||||
<# for(var i = 0; i < RegisterInfos.Length; i++) { var x = RegisterInfos[i]; #>
|
||||
case <#= i #>: return new <#= x.FormatterName.StartsWith("global::") ? x.FormatterName: (!string.IsNullOrEmpty(FormatterNamespace) ? FormatterNamespace + "." : FormatterNamespace) + x.FormatterName#>();
|
||||
<# } #>
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma warning restore 168
|
||||
#pragma warning restore 414
|
||||
#pragma warning restore 618
|
||||
#pragma warning restore 612
|
||||
|
||||
#pragma warning restore SA1312 // Variable names should begin with lower-case letter
|
||||
#pragma warning restore SA1200 // Using directives should be placed correctly
|
||||
#pragma warning restore SA1649 // File name should match first type name
|
|
@ -0,0 +1,44 @@
|
|||
// 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 System.Threading.Tasks;
|
||||
using MessagePackCompiler.CodeAnalysis;
|
||||
|
||||
namespace MessagePackCompiler.Generator
|
||||
{
|
||||
public partial class FormatterTemplate
|
||||
{
|
||||
public string Namespace { get; set; }
|
||||
|
||||
public ObjectSerializationInfo[] ObjectSerializationInfos { get; set; }
|
||||
}
|
||||
|
||||
public partial class ResolverTemplate
|
||||
{
|
||||
public string Namespace { get; set; }
|
||||
|
||||
public string FormatterNamespace { get; set; }
|
||||
|
||||
public string ResolverName { get; set; } = "GeneratedResolver";
|
||||
|
||||
public IResolverRegisterInfo[] RegisterInfos { get; set; }
|
||||
}
|
||||
|
||||
public partial class EnumTemplate
|
||||
{
|
||||
public string Namespace { get; set; }
|
||||
|
||||
public EnumSerializationInfo[] EnumSerializationInfos { get; set; }
|
||||
}
|
||||
|
||||
public partial class UnionTemplate
|
||||
{
|
||||
public string Namespace { get; set; }
|
||||
|
||||
public UnionSerializationInfo[] UnionSerializationInfos { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,415 @@
|
|||
// ------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// このコードはツールによって生成されました。
|
||||
// ランタイム バージョン: 16.0.0.0
|
||||
//
|
||||
// このファイルへの変更は、正しくない動作の原因になる可能性があり、
|
||||
// コードが再生成されると失われます。
|
||||
// </auto-generated>
|
||||
// ------------------------------------------------------------------------------
|
||||
namespace MessagePackCompiler.Generator
|
||||
{
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
|
||||
/// <summary>
|
||||
/// Class to produce the template output
|
||||
/// </summary>
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "16.0.0.0")]
|
||||
public partial class UnionTemplate : UnionTemplateBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Create the template output
|
||||
/// </summary>
|
||||
public virtual string TransformText()
|
||||
{
|
||||
this.Write(@"#pragma warning disable 618
|
||||
#pragma warning disable 612
|
||||
#pragma warning disable 414
|
||||
#pragma warning disable 168
|
||||
|
||||
#pragma warning disable SA1200 // Using directives should be placed correctly
|
||||
#pragma warning disable SA1403 // File may only contain a single namespace
|
||||
#pragma warning disable SA1649 // File name should match first type name
|
||||
|
||||
namespace ");
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(Namespace));
|
||||
this.Write("\r\n{\r\n using System;\r\n using System.Buffers;\r\n using System.Collections.G" +
|
||||
"eneric;\r\n using MessagePack;\r\n\r\n");
|
||||
foreach(var info in UnionSerializationInfos) {
|
||||
this.Write(" public sealed class ");
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(info.Name));
|
||||
this.Write("Formatter : global::MessagePack.Formatters.IMessagePackFormatter<");
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(info.FullName));
|
||||
this.Write(">\r\n {\r\n private readonly Dictionary<RuntimeTypeHandle, KeyValuePair<int" +
|
||||
", int>> typeToKeyAndJumpMap;\r\n private readonly Dictionary<int, int> keyT" +
|
||||
"oJumpMap;\r\n\r\n public ");
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(info.Name));
|
||||
this.Write("Formatter()\r\n {\r\n this.typeToKeyAndJumpMap = new Dictionary<Run" +
|
||||
"timeTypeHandle, KeyValuePair<int, int>>(");
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(info.SubTypes.Length));
|
||||
this.Write(", global::MessagePack.Internal.RuntimeTypeHandleEqualityComparer.Default)\r\n " +
|
||||
" {\r\n");
|
||||
for(var i = 0; i < info.SubTypes.Length; i++) { var item = info.SubTypes[i];
|
||||
this.Write(" { typeof(");
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(item.Type));
|
||||
this.Write(").TypeHandle, new KeyValuePair<int, int>(");
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(item.Key));
|
||||
this.Write(", ");
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(i));
|
||||
this.Write(") },\r\n");
|
||||
}
|
||||
this.Write(" };\r\n this.keyToJumpMap = new Dictionary<int, int>(");
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(info.SubTypes.Length));
|
||||
this.Write(")\r\n {\r\n");
|
||||
for(var i = 0; i < info.SubTypes.Length; i++) { var item = info.SubTypes[i];
|
||||
this.Write(" { ");
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(item.Key));
|
||||
this.Write(", ");
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(i));
|
||||
this.Write(" },\r\n");
|
||||
}
|
||||
this.Write(" };\r\n }\r\n\r\n public void Serialize(ref MessagePackWriter " +
|
||||
"writer, ");
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(info.FullName));
|
||||
this.Write(@" value, global::MessagePack.MessagePackSerializerOptions options)
|
||||
{
|
||||
KeyValuePair<int, int> keyValuePair;
|
||||
if (value != null && this.typeToKeyAndJumpMap.TryGetValue(value.GetType().TypeHandle, out keyValuePair))
|
||||
{
|
||||
writer.WriteArrayHeader(2);
|
||||
writer.WriteInt32(keyValuePair.Key);
|
||||
switch (keyValuePair.Value)
|
||||
{
|
||||
");
|
||||
for(var i = 0; i < info.SubTypes.Length; i++) { var item = info.SubTypes[i];
|
||||
this.Write(" case ");
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(i));
|
||||
this.Write(":\r\n options.Resolver.GetFormatterWithVerify<");
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(item.Type));
|
||||
this.Write(">().Serialize(ref writer, (");
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(item.Type));
|
||||
this.Write(")value, options);\r\n break;\r\n");
|
||||
}
|
||||
this.Write(" default:\r\n break;\r\n }\r\n" +
|
||||
"\r\n return;\r\n }\r\n\r\n writer.WriteNil();\r\n " +
|
||||
" }\r\n\r\n public ");
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(info.FullName));
|
||||
this.Write(@" Deserialize(ref MessagePackReader reader, global::MessagePack.MessagePackSerializerOptions options)
|
||||
{
|
||||
if (reader.TryReadNil())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (reader.ReadArrayHeader() != 2)
|
||||
{
|
||||
throw new InvalidOperationException(""Invalid Union data was detected. Type:");
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(info.FullName));
|
||||
this.Write("\");\r\n }\r\n\r\n var key = reader.ReadInt32();\r\n\r\n if" +
|
||||
" (!this.keyToJumpMap.TryGetValue(key, out key))\r\n {\r\n " +
|
||||
"key = -1;\r\n }\r\n\r\n ");
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(info.FullName));
|
||||
this.Write(" result = null;\r\n switch (key)\r\n {\r\n");
|
||||
for(var i = 0; i < info.SubTypes.Length; i++) { var item = info.SubTypes[i];
|
||||
this.Write(" case ");
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(i));
|
||||
this.Write(":\r\n result = (");
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(info.FullName));
|
||||
this.Write(")options.Resolver.GetFormatterWithVerify<");
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(item.Type));
|
||||
this.Write(">().Deserialize(ref reader, options);\r\n break;\r\n");
|
||||
}
|
||||
this.Write(" default:\r\n reader.Skip();\r\n " +
|
||||
" break;\r\n }\r\n\r\n return result;\r\n }\r\n }\r\n\r\n");
|
||||
}
|
||||
this.Write(@"
|
||||
}
|
||||
|
||||
#pragma warning restore 168
|
||||
#pragma warning restore 414
|
||||
#pragma warning restore 618
|
||||
#pragma warning restore 612
|
||||
|
||||
#pragma warning restore SA1200 // Using directives should be placed correctly
|
||||
#pragma warning restore SA1403 // File may only contain a single namespace
|
||||
#pragma warning restore SA1649 // File name should match first type name
|
||||
");
|
||||
return this.GenerationEnvironment.ToString();
|
||||
}
|
||||
}
|
||||
#region Base class
|
||||
/// <summary>
|
||||
/// Base class for this transformation
|
||||
/// </summary>
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "16.0.0.0")]
|
||||
public class UnionTemplateBase
|
||||
{
|
||||
#region Fields
|
||||
private global::System.Text.StringBuilder generationEnvironmentField;
|
||||
private global::System.CodeDom.Compiler.CompilerErrorCollection errorsField;
|
||||
private global::System.Collections.Generic.List<int> indentLengthsField;
|
||||
private string currentIndentField = "";
|
||||
private bool endsWithNewline;
|
||||
private global::System.Collections.Generic.IDictionary<string, object> sessionField;
|
||||
#endregion
|
||||
#region Properties
|
||||
/// <summary>
|
||||
/// The string builder that generation-time code is using to assemble generated output
|
||||
/// </summary>
|
||||
protected System.Text.StringBuilder GenerationEnvironment
|
||||
{
|
||||
get
|
||||
{
|
||||
if ((this.generationEnvironmentField == null))
|
||||
{
|
||||
this.generationEnvironmentField = new global::System.Text.StringBuilder();
|
||||
}
|
||||
return this.generationEnvironmentField;
|
||||
}
|
||||
set
|
||||
{
|
||||
this.generationEnvironmentField = value;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// The error collection for the generation process
|
||||
/// </summary>
|
||||
public System.CodeDom.Compiler.CompilerErrorCollection Errors
|
||||
{
|
||||
get
|
||||
{
|
||||
if ((this.errorsField == null))
|
||||
{
|
||||
this.errorsField = new global::System.CodeDom.Compiler.CompilerErrorCollection();
|
||||
}
|
||||
return this.errorsField;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// A list of the lengths of each indent that was added with PushIndent
|
||||
/// </summary>
|
||||
private System.Collections.Generic.List<int> indentLengths
|
||||
{
|
||||
get
|
||||
{
|
||||
if ((this.indentLengthsField == null))
|
||||
{
|
||||
this.indentLengthsField = new global::System.Collections.Generic.List<int>();
|
||||
}
|
||||
return this.indentLengthsField;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets the current indent we use when adding lines to the output
|
||||
/// </summary>
|
||||
public string CurrentIndent
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.currentIndentField;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Current transformation session
|
||||
/// </summary>
|
||||
public virtual global::System.Collections.Generic.IDictionary<string, object> Session
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.sessionField;
|
||||
}
|
||||
set
|
||||
{
|
||||
this.sessionField = value;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
#region Transform-time helpers
|
||||
/// <summary>
|
||||
/// Write text directly into the generated output
|
||||
/// </summary>
|
||||
public void Write(string textToAppend)
|
||||
{
|
||||
if (string.IsNullOrEmpty(textToAppend))
|
||||
{
|
||||
return;
|
||||
}
|
||||
// If we're starting off, or if the previous text ended with a newline,
|
||||
// we have to append the current indent first.
|
||||
if (((this.GenerationEnvironment.Length == 0)
|
||||
|| this.endsWithNewline))
|
||||
{
|
||||
this.GenerationEnvironment.Append(this.currentIndentField);
|
||||
this.endsWithNewline = false;
|
||||
}
|
||||
// Check if the current text ends with a newline
|
||||
if (textToAppend.EndsWith(global::System.Environment.NewLine, global::System.StringComparison.CurrentCulture))
|
||||
{
|
||||
this.endsWithNewline = true;
|
||||
}
|
||||
// This is an optimization. If the current indent is "", then we don't have to do any
|
||||
// of the more complex stuff further down.
|
||||
if ((this.currentIndentField.Length == 0))
|
||||
{
|
||||
this.GenerationEnvironment.Append(textToAppend);
|
||||
return;
|
||||
}
|
||||
// Everywhere there is a newline in the text, add an indent after it
|
||||
textToAppend = textToAppend.Replace(global::System.Environment.NewLine, (global::System.Environment.NewLine + this.currentIndentField));
|
||||
// If the text ends with a newline, then we should strip off the indent added at the very end
|
||||
// because the appropriate indent will be added when the next time Write() is called
|
||||
if (this.endsWithNewline)
|
||||
{
|
||||
this.GenerationEnvironment.Append(textToAppend, 0, (textToAppend.Length - this.currentIndentField.Length));
|
||||
}
|
||||
else
|
||||
{
|
||||
this.GenerationEnvironment.Append(textToAppend);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Write text directly into the generated output
|
||||
/// </summary>
|
||||
public void WriteLine(string textToAppend)
|
||||
{
|
||||
this.Write(textToAppend);
|
||||
this.GenerationEnvironment.AppendLine();
|
||||
this.endsWithNewline = true;
|
||||
}
|
||||
/// <summary>
|
||||
/// Write formatted text directly into the generated output
|
||||
/// </summary>
|
||||
public void Write(string format, params object[] args)
|
||||
{
|
||||
this.Write(string.Format(global::System.Globalization.CultureInfo.CurrentCulture, format, args));
|
||||
}
|
||||
/// <summary>
|
||||
/// Write formatted text directly into the generated output
|
||||
/// </summary>
|
||||
public void WriteLine(string format, params object[] args)
|
||||
{
|
||||
this.WriteLine(string.Format(global::System.Globalization.CultureInfo.CurrentCulture, format, args));
|
||||
}
|
||||
/// <summary>
|
||||
/// Raise an error
|
||||
/// </summary>
|
||||
public void Error(string message)
|
||||
{
|
||||
System.CodeDom.Compiler.CompilerError error = new global::System.CodeDom.Compiler.CompilerError();
|
||||
error.ErrorText = message;
|
||||
this.Errors.Add(error);
|
||||
}
|
||||
/// <summary>
|
||||
/// Raise a warning
|
||||
/// </summary>
|
||||
public void Warning(string message)
|
||||
{
|
||||
System.CodeDom.Compiler.CompilerError error = new global::System.CodeDom.Compiler.CompilerError();
|
||||
error.ErrorText = message;
|
||||
error.IsWarning = true;
|
||||
this.Errors.Add(error);
|
||||
}
|
||||
/// <summary>
|
||||
/// Increase the indent
|
||||
/// </summary>
|
||||
public void PushIndent(string indent)
|
||||
{
|
||||
if ((indent == null))
|
||||
{
|
||||
throw new global::System.ArgumentNullException("indent");
|
||||
}
|
||||
this.currentIndentField = (this.currentIndentField + indent);
|
||||
this.indentLengths.Add(indent.Length);
|
||||
}
|
||||
/// <summary>
|
||||
/// Remove the last indent that was added with PushIndent
|
||||
/// </summary>
|
||||
public string PopIndent()
|
||||
{
|
||||
string returnValue = "";
|
||||
if ((this.indentLengths.Count > 0))
|
||||
{
|
||||
int indentLength = this.indentLengths[(this.indentLengths.Count - 1)];
|
||||
this.indentLengths.RemoveAt((this.indentLengths.Count - 1));
|
||||
if ((indentLength > 0))
|
||||
{
|
||||
returnValue = this.currentIndentField.Substring((this.currentIndentField.Length - indentLength));
|
||||
this.currentIndentField = this.currentIndentField.Remove((this.currentIndentField.Length - indentLength));
|
||||
}
|
||||
}
|
||||
return returnValue;
|
||||
}
|
||||
/// <summary>
|
||||
/// Remove any indentation
|
||||
/// </summary>
|
||||
public void ClearIndent()
|
||||
{
|
||||
this.indentLengths.Clear();
|
||||
this.currentIndentField = "";
|
||||
}
|
||||
#endregion
|
||||
#region ToString Helpers
|
||||
/// <summary>
|
||||
/// Utility class to produce culture-oriented representation of an object as a string.
|
||||
/// </summary>
|
||||
public class ToStringInstanceHelper
|
||||
{
|
||||
private System.IFormatProvider formatProviderField = global::System.Globalization.CultureInfo.InvariantCulture;
|
||||
/// <summary>
|
||||
/// Gets or sets format provider to be used by ToStringWithCulture method.
|
||||
/// </summary>
|
||||
public System.IFormatProvider FormatProvider
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.formatProviderField ;
|
||||
}
|
||||
set
|
||||
{
|
||||
if ((value != null))
|
||||
{
|
||||
this.formatProviderField = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// This is called from the compile/run appdomain to convert objects within an expression block to a string
|
||||
/// </summary>
|
||||
public string ToStringWithCulture(object objectToConvert)
|
||||
{
|
||||
if ((objectToConvert == null))
|
||||
{
|
||||
throw new global::System.ArgumentNullException("objectToConvert");
|
||||
}
|
||||
System.Type t = objectToConvert.GetType();
|
||||
System.Reflection.MethodInfo method = t.GetMethod("ToString", new System.Type[] {
|
||||
typeof(System.IFormatProvider)});
|
||||
if ((method == null))
|
||||
{
|
||||
return objectToConvert.ToString();
|
||||
}
|
||||
else
|
||||
{
|
||||
return ((string)(method.Invoke(objectToConvert, new object[] {
|
||||
this.formatProviderField })));
|
||||
}
|
||||
}
|
||||
}
|
||||
private ToStringInstanceHelper toStringHelperField = new ToStringInstanceHelper();
|
||||
/// <summary>
|
||||
/// Helper to produce culture-oriented representation of an object as a string
|
||||
/// </summary>
|
||||
public ToStringInstanceHelper ToStringHelper
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.toStringHelperField;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
#endregion
|
||||
}
|
|
@ -0,0 +1,115 @@
|
|||
<#@ template debug="false" hostspecific="false" linePragmas="false" language="C#" #>
|
||||
<#@ assembly name="System.Core" #>
|
||||
<#@ import namespace="System.Linq" #>
|
||||
<#@ import namespace="System.Text" #>
|
||||
<#@ import namespace="System.Collections.Generic" #>
|
||||
#pragma warning disable 618
|
||||
#pragma warning disable 612
|
||||
#pragma warning disable 414
|
||||
#pragma warning disable 168
|
||||
|
||||
#pragma warning disable SA1200 // Using directives should be placed correctly
|
||||
#pragma warning disable SA1403 // File may only contain a single namespace
|
||||
#pragma warning disable SA1649 // File name should match first type name
|
||||
|
||||
namespace <#= Namespace #>
|
||||
{
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Collections.Generic;
|
||||
using MessagePack;
|
||||
|
||||
<# foreach(var info in UnionSerializationInfos) { #>
|
||||
public sealed class <#= info.Name #>Formatter : global::MessagePack.Formatters.IMessagePackFormatter<<#= info.FullName #>>
|
||||
{
|
||||
private readonly Dictionary<RuntimeTypeHandle, KeyValuePair<int, int>> typeToKeyAndJumpMap;
|
||||
private readonly Dictionary<int, int> keyToJumpMap;
|
||||
|
||||
public <#= info.Name #>Formatter()
|
||||
{
|
||||
this.typeToKeyAndJumpMap = new Dictionary<RuntimeTypeHandle, KeyValuePair<int, int>>(<#= info.SubTypes.Length #>, global::MessagePack.Internal.RuntimeTypeHandleEqualityComparer.Default)
|
||||
{
|
||||
<# for(var i = 0; i < info.SubTypes.Length; i++) { var item = info.SubTypes[i]; #>
|
||||
{ typeof(<#= item.Type #>).TypeHandle, new KeyValuePair<int, int>(<#= item.Key #>, <#= i #>) },
|
||||
<# } #>
|
||||
};
|
||||
this.keyToJumpMap = new Dictionary<int, int>(<#= info.SubTypes.Length #>)
|
||||
{
|
||||
<# for(var i = 0; i < info.SubTypes.Length; i++) { var item = info.SubTypes[i]; #>
|
||||
{ <#= item.Key #>, <#= i #> },
|
||||
<# } #>
|
||||
};
|
||||
}
|
||||
|
||||
public void Serialize(ref MessagePackWriter writer, <#= info.FullName #> value, global::MessagePack.MessagePackSerializerOptions options)
|
||||
{
|
||||
KeyValuePair<int, int> keyValuePair;
|
||||
if (value != null && this.typeToKeyAndJumpMap.TryGetValue(value.GetType().TypeHandle, out keyValuePair))
|
||||
{
|
||||
writer.WriteArrayHeader(2);
|
||||
writer.WriteInt32(keyValuePair.Key);
|
||||
switch (keyValuePair.Value)
|
||||
{
|
||||
<# for(var i = 0; i < info.SubTypes.Length; i++) { var item = info.SubTypes[i]; #>
|
||||
case <#= i #>:
|
||||
options.Resolver.GetFormatterWithVerify<<#= item.Type #>>().Serialize(ref writer, (<#= item.Type #>)value, options);
|
||||
break;
|
||||
<# } #>
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
writer.WriteNil();
|
||||
}
|
||||
|
||||
public <#= info.FullName #> Deserialize(ref MessagePackReader reader, global::MessagePack.MessagePackSerializerOptions options)
|
||||
{
|
||||
if (reader.TryReadNil())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (reader.ReadArrayHeader() != 2)
|
||||
{
|
||||
throw new InvalidOperationException("Invalid Union data was detected. Type:<#= info.FullName #>");
|
||||
}
|
||||
|
||||
var key = reader.ReadInt32();
|
||||
|
||||
if (!this.keyToJumpMap.TryGetValue(key, out key))
|
||||
{
|
||||
key = -1;
|
||||
}
|
||||
|
||||
<#= info.FullName #> result = null;
|
||||
switch (key)
|
||||
{
|
||||
<# for(var i = 0; i < info.SubTypes.Length; i++) { var item = info.SubTypes[i]; #>
|
||||
case <#= i #>:
|
||||
result = (<#= info.FullName #>)options.Resolver.GetFormatterWithVerify<<#= item.Type #>>().Deserialize(ref reader, options);
|
||||
break;
|
||||
<# } #>
|
||||
default:
|
||||
reader.Skip();
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
<# } #>
|
||||
|
||||
}
|
||||
|
||||
#pragma warning restore 168
|
||||
#pragma warning restore 414
|
||||
#pragma warning restore 618
|
||||
#pragma warning restore 612
|
||||
|
||||
#pragma warning restore SA1200 // Using directives should be placed correctly
|
||||
#pragma warning restore SA1403 // File may only contain a single namespace
|
||||
#pragma warning restore SA1649 // File name should match first type name
|
|
@ -0,0 +1,66 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<RootNamespace>MessagePackCompiler</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.1.0" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="3.1.0" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.Workspaces.Common" Version="3.1.0" />
|
||||
<PackageReference Include="System.CodeDom" Version="4.5.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Update="Generator\EnumTemplate.cs">
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>EnumTemplate.tt</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Update="Generator\FormatterTemplate.cs">
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>FormatterTemplate.tt</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Update="Generator\ResolverTemplate.cs">
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>ResolverTemplate.tt</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Update="Generator\TemplatePartials.cs">
|
||||
<DependentUpon>%(FileName).tt</DependentUpon>
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
</Compile>
|
||||
<Compile Update="Generator\UnionTemplate.cs">
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>UnionTemplate.tt</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="Generator\EnumTemplate.tt">
|
||||
<LastGenOutput>EnumTemplate.cs</LastGenOutput>
|
||||
<Generator>TextTemplatingFilePreprocessor</Generator>
|
||||
</None>
|
||||
<None Update="Generator\FormatterTemplate.tt">
|
||||
<LastGenOutput>FormatterTemplate.cs</LastGenOutput>
|
||||
<Generator>TextTemplatingFilePreprocessor</Generator>
|
||||
</None>
|
||||
<None Update="Generator\ResolverTemplate.tt">
|
||||
<LastGenOutput>ResolverTemplate.cs</LastGenOutput>
|
||||
<Generator>TextTemplatingFilePreprocessor</Generator>
|
||||
</None>
|
||||
<None Update="Generator\UnionTemplate.tt">
|
||||
<LastGenOutput>UnionTemplate.cs</LastGenOutput>
|
||||
<Generator>TextTemplatingFilePreprocessor</Generator>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Service Include="{508349b6-6b84-4df5-91f0-309beebad82d}" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -0,0 +1,316 @@
|
|||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
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 System.Xml;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace MessagePackCompiler
|
||||
{
|
||||
public class MessagePackCompilation
|
||||
{
|
||||
public static async Task<CSharpCompilation> CreateFromProjectAsync(string[] csprojs, string[] preprocessorSymbols, CancellationToken cancellationToken)
|
||||
{
|
||||
var parseOption = new CSharpParseOptions(LanguageVersion.Latest, DocumentationMode.Parse, SourceCodeKind.Regular, preprocessorSymbols);
|
||||
var syntaxTrees = new List<SyntaxTree>();
|
||||
var metadata = new[]
|
||||
{
|
||||
typeof(object),
|
||||
typeof(Enumerable),
|
||||
typeof(Task<>),
|
||||
typeof(IgnoreDataMemberAttribute),
|
||||
typeof(System.Collections.Generic.List<>),
|
||||
typeof(System.Collections.Concurrent.ConcurrentDictionary<,>),
|
||||
}
|
||||
.Select(x => x.Assembly.Location)
|
||||
.Distinct()
|
||||
.Select(x => MetadataReference.CreateFromFile(x))
|
||||
.ToList();
|
||||
|
||||
var sources = new List<string>();
|
||||
var locations = new List<string>();
|
||||
foreach (var csproj in csprojs)
|
||||
{
|
||||
CollectDocument(csproj, sources, locations);
|
||||
}
|
||||
|
||||
var hasAnnotations = false;
|
||||
foreach (var file in sources.Distinct())
|
||||
{
|
||||
var text = File.ReadAllText(file.Replace('\\', Path.DirectorySeparatorChar), Encoding.UTF8);
|
||||
var syntax = CSharpSyntaxTree.ParseText(text, parseOption);
|
||||
syntaxTrees.Add(syntax);
|
||||
if (Path.GetFileNameWithoutExtension(file) == "Attributes")
|
||||
{
|
||||
var root = await syntax.GetRootAsync(cancellationToken);
|
||||
if (root.DescendantNodes().OfType<ClassDeclarationSyntax>().Any(x => x.Identifier.Text == "MessagePackObjectAttribute"))
|
||||
{
|
||||
hasAnnotations = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!hasAnnotations)
|
||||
{
|
||||
syntaxTrees.Add(CSharpSyntaxTree.ParseText(DummyAnnotation, parseOption));
|
||||
}
|
||||
|
||||
var nazoloc = locations.Distinct();
|
||||
foreach (var item in locations.Distinct().Where(x => !x.Contains("MonoBleedingEdge")))
|
||||
{
|
||||
metadata.Add(MetadataReference.CreateFromFile(item));
|
||||
}
|
||||
|
||||
var compilation = CSharpCompilation.Create(
|
||||
"MessagepackCodeGenTemp",
|
||||
syntaxTrees,
|
||||
metadata,
|
||||
new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary, allowUnsafe: true));
|
||||
|
||||
return compilation;
|
||||
}
|
||||
|
||||
static void CollectDocument(string csproj, List<string> source, List<string> metadataLocations)
|
||||
{
|
||||
XDocument document;
|
||||
using (var sr = new StreamReader(csproj, true))
|
||||
{
|
||||
var reader = new XmlTextReader(sr);
|
||||
reader.Namespaces = false;
|
||||
|
||||
document = XDocument.Load(reader, LoadOptions.None);
|
||||
}
|
||||
|
||||
var csProjRoot = Path.GetDirectoryName(csproj);
|
||||
var framworkRoot = Path.GetDirectoryName(typeof(object).Assembly.Location);
|
||||
|
||||
// Legacy
|
||||
// <Project ToolsVersion=...>
|
||||
// New
|
||||
// <Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
var proj = document.Element("Project");
|
||||
var legacyFormat = proj.Attribute("Sdk")?.Value != "Microsoft.NET.Sdk";
|
||||
|
||||
if (!legacyFormat)
|
||||
{
|
||||
// TODO:parase compile remove
|
||||
foreach (var file in IterateCsFileWithoutBinObj(Path.GetDirectoryName(csproj)))
|
||||
{
|
||||
source.Add(file);
|
||||
}
|
||||
// TODO:resolve NuGet reference
|
||||
// RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
|
||||
}
|
||||
|
||||
{
|
||||
// files
|
||||
foreach (var item in document.Descendants("Compile"))
|
||||
{
|
||||
var include = item.Attribute("Include")?.Value;
|
||||
if (include != null)
|
||||
{
|
||||
source.Add(Path.Combine(csProjRoot, include));
|
||||
}
|
||||
}
|
||||
|
||||
// shared
|
||||
foreach (var item in document.Descendants("Import"))
|
||||
{
|
||||
if (item.Attribute("Label")?.Value == "Shared")
|
||||
{
|
||||
var sharedRoot = Path.GetDirectoryName(Path.Combine(csProjRoot, item.Attribute("Project").Value));
|
||||
foreach (var file in IterateCsFileWithoutBinObj(Path.GetDirectoryName(sharedRoot)))
|
||||
{
|
||||
source.Add(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// proj-ref
|
||||
foreach (var item in document.Descendants("ProjectReference"))
|
||||
{
|
||||
var refCsProjPath = item.Attribute("Include")?.Value;
|
||||
if (refCsProjPath != null)
|
||||
{
|
||||
CollectDocument(Path.Combine(csProjRoot, refCsProjPath), source, metadataLocations);
|
||||
}
|
||||
}
|
||||
|
||||
// metadata
|
||||
foreach (var item in document.Descendants("Reference"))
|
||||
{
|
||||
var hintPath = item.Element("HintPath")?.Value;
|
||||
if (hintPath == null)
|
||||
{
|
||||
var path = Path.Combine(framworkRoot, item.Attribute("Include").Value + ".dll");
|
||||
metadataLocations.Add(path);
|
||||
}
|
||||
else
|
||||
{
|
||||
metadataLocations.Add(Path.Combine(csProjRoot, hintPath));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task<CSharpCompilation> CreateFromDirectoryAsync(string directoryRoot, string[] preprocessorSymbols, CancellationToken cancellationToken)
|
||||
{
|
||||
var parseOption = new CSharpParseOptions(LanguageVersion.Latest, DocumentationMode.Parse, SourceCodeKind.Regular, preprocessorSymbols);
|
||||
|
||||
var hasAnnotations = false;
|
||||
var syntaxTrees = new List<SyntaxTree>();
|
||||
foreach (var file in IterateCsFileWithoutBinObj(directoryRoot))
|
||||
{
|
||||
var text = File.ReadAllText(file.Replace('\\', Path.DirectorySeparatorChar), Encoding.UTF8);
|
||||
var syntax = CSharpSyntaxTree.ParseText(text, parseOption);
|
||||
syntaxTrees.Add(syntax);
|
||||
if (Path.GetFileNameWithoutExtension(file) == "Attributes")
|
||||
{
|
||||
var root = await syntax.GetRootAsync(cancellationToken);
|
||||
if (root.DescendantNodes().OfType<ClassDeclarationSyntax>().Any(x => x.Identifier.Text == "MessagePackObjectAttribute"))
|
||||
{
|
||||
hasAnnotations = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!hasAnnotations)
|
||||
{
|
||||
syntaxTrees.Add(CSharpSyntaxTree.ParseText(DummyAnnotation, parseOption));
|
||||
}
|
||||
|
||||
var metadata = new[]
|
||||
{
|
||||
typeof(object),
|
||||
typeof(Enumerable),
|
||||
typeof(Task<>),
|
||||
typeof(IgnoreDataMemberAttribute),
|
||||
typeof(System.Collections.Generic.List<>),
|
||||
typeof(System.Collections.Concurrent.ConcurrentDictionary<,>),
|
||||
}
|
||||
.Select(x => x.Assembly.Location)
|
||||
.Distinct()
|
||||
.Select(x => MetadataReference.CreateFromFile(x))
|
||||
.ToList();
|
||||
|
||||
var compilation = CSharpCompilation.Create(
|
||||
"MessagepackCodeGenTemp",
|
||||
syntaxTrees,
|
||||
metadata,
|
||||
new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary, allowUnsafe: true));
|
||||
|
||||
return compilation;
|
||||
}
|
||||
|
||||
static IEnumerable<string> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,135 @@
|
|||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace MessagePackCompiler
|
||||
{
|
||||
// Utility and Extension methods for Roslyn
|
||||
internal static class RoslynExtensions
|
||||
{
|
||||
public static IEnumerable<INamedTypeSymbol> GetNamedTypeSymbols(this Compilation compilation)
|
||||
{
|
||||
foreach (var syntaxTree in compilation.SyntaxTrees)
|
||||
{
|
||||
var semModel = compilation.GetSemanticModel(syntaxTree);
|
||||
|
||||
foreach (var item in syntaxTree.GetRoot()
|
||||
.DescendantNodes()
|
||||
.Select(x => semModel.GetDeclaredSymbol(x))
|
||||
.Where(x => x != null))
|
||||
{
|
||||
var namedType = item as INamedTypeSymbol;
|
||||
if (namedType != null)
|
||||
{
|
||||
yield return namedType;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<INamedTypeSymbol> EnumerateBaseType(this ITypeSymbol symbol)
|
||||
{
|
||||
var t = symbol.BaseType;
|
||||
while (t != null)
|
||||
{
|
||||
yield return t;
|
||||
t = t.BaseType;
|
||||
}
|
||||
}
|
||||
|
||||
public static AttributeData FindAttribute(this IEnumerable<AttributeData> attributeDataList, string typeName)
|
||||
{
|
||||
return attributeDataList
|
||||
.Where(x => x.AttributeClass.ToDisplayString() == typeName)
|
||||
.FirstOrDefault();
|
||||
}
|
||||
|
||||
public static AttributeData FindAttributeShortName(this IEnumerable<AttributeData> attributeDataList,
|
||||
string typeName)
|
||||
{
|
||||
return attributeDataList
|
||||
.Where(x => x.AttributeClass.Name == typeName)
|
||||
.FirstOrDefault();
|
||||
}
|
||||
|
||||
public static AttributeData FindAttributeIncludeBasePropertyShortName(this IPropertySymbol property,
|
||||
string typeName)
|
||||
{
|
||||
do
|
||||
{
|
||||
var data = FindAttributeShortName(property.GetAttributes(), typeName);
|
||||
if (data != null) return data;
|
||||
property = property.OverriddenProperty;
|
||||
} while (property != null);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static AttributeSyntax FindAttribute(this BaseTypeDeclarationSyntax typeDeclaration, SemanticModel model,
|
||||
string typeName)
|
||||
{
|
||||
return typeDeclaration.AttributeLists
|
||||
.SelectMany(x => x.Attributes)
|
||||
.Where(x => model.GetTypeInfo(x).Type?.ToDisplayString() == typeName)
|
||||
.FirstOrDefault();
|
||||
}
|
||||
|
||||
public static INamedTypeSymbol FindBaseTargetType(this ITypeSymbol symbol, string typeName)
|
||||
{
|
||||
return symbol.EnumerateBaseType()
|
||||
.Where(x => x.OriginalDefinition?.ToDisplayString() == typeName)
|
||||
.FirstOrDefault();
|
||||
}
|
||||
|
||||
public static object GetSingleNamedArgumentValue(this AttributeData attribute, string key)
|
||||
{
|
||||
foreach (var item in attribute.NamedArguments)
|
||||
{
|
||||
if (item.Key == key)
|
||||
{
|
||||
return item.Value.Value;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static bool IsNullable(this INamedTypeSymbol symbol)
|
||||
{
|
||||
if (symbol.IsGenericType)
|
||||
{
|
||||
if (symbol.ConstructUnboundGenericType().ToDisplayString() == "T?")
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static IEnumerable<ISymbol> GetAllMembers(this ITypeSymbol symbol)
|
||||
{
|
||||
var t = symbol;
|
||||
while (t != null)
|
||||
{
|
||||
foreach (var item in t.GetMembers())
|
||||
{
|
||||
yield return item;
|
||||
}
|
||||
t = t.BaseType;
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<ISymbol> GetAllInterfaceMembers(this ITypeSymbol symbol)
|
||||
{
|
||||
return symbol.GetMembers()
|
||||
.Concat(symbol.AllInterfaces.SelectMany(x => x.GetMembers()));
|
||||
}
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче