From fa9ef8181a31fc8f25337d9ad1f96e32f15ab23f Mon Sep 17 00:00:00 2001 From: "Anapher (Vincent)" Date: Sun, 13 Aug 2017 20:23:13 +0200 Subject: [PATCH 1/2] Add explicit formatter declaration for attribute * add tests * add checks in analyzer --- .../ZeroFormatterAnalyzer.cs | 27 ++++++++- src/ZeroFormatter.Interfaces/Attributes.cs | 1 + src/ZeroFormatter/Formatters/Formatter.cs | 28 +++++++++- .../ZeroFormatter.Tests.csproj | 1 + .../ZeroFormatterAttributeTest.cs | 55 +++++++++++++++++++ 5 files changed, 108 insertions(+), 4 deletions(-) create mode 100644 tests/ZeroFormatter.Tests/ZeroFormatterAttributeTest.cs diff --git a/src/ZeroFormatter.Analyzer/ZeroFormatterAnalyzer.cs b/src/ZeroFormatter.Analyzer/ZeroFormatterAnalyzer.cs index 0039c15..31c38a5 100644 --- a/src/ZeroFormatter.Analyzer/ZeroFormatterAnalyzer.cs +++ b/src/ZeroFormatter.Analyzer/ZeroFormatterAnalyzer.cs @@ -23,6 +23,8 @@ public class ZeroFormatterAnalyzer : DiagnosticAnalyzer internal const string UnionAttributeShortName = "UnionAttribute"; internal const string DynamicUnionAttributeShortName = "DynamicUnionAttribute"; internal const string UnionKeyAttributeShortName = "UnionKeyAttribute"; + internal const string ZeroFormattableFormatterProperty = "FormatterType"; + internal const string FormatterTypeName = "Formatter"; internal static readonly DiagnosticDescriptor TypeMustBeZeroFormattable = new DiagnosticDescriptor( id: DiagnosticIdBase + "_" + nameof(TypeMustBeZeroFormattable), title: Title, category: Category, @@ -102,6 +104,12 @@ public class ZeroFormatterAnalyzer : DiagnosticAnalyzer description: "All Union subTypes must be inherited type.", defaultSeverity: DiagnosticSeverity.Error, isEnabledByDefault: true); + internal static readonly DiagnosticDescriptor ExplicitFormatterMustInheritFormatter = new DiagnosticDescriptor( + id: DiagnosticIdBase + "_" + nameof(ExplicitFormatterMustInheritFormatter), title: Title, category: Category, + messageFormat: "Explicit Formatter type must inherit from Formatter<>. Type: {0}, Formatter: {1}", // type.Name subType.Name + description: "Explicit Formatter type must inherit from Formatter<>.", + defaultSeverity: DiagnosticSeverity.Error, isEnabledByDefault: true); + static readonly ImmutableArray supportedDiagnostics = ImmutableArray.Create( TypeMustBeZeroFormattable, PublicPropertyNeedsIndex, @@ -164,9 +172,9 @@ static void Analyze(SyntaxNodeAnalysisContext context) if (declaredSymbol == null) return; var isUnion = declaredSymbol.GetAttributes().FindAttributeShortName(UnionAttributeShortName) != null; - var isZeroFormattable = declaredSymbol.GetAttributes().FindAttributeShortName(ZeroFormattableAttributeShortName) != null; + var zeroFormattableAttribute = declaredSymbol.GetAttributes().FindAttributeShortName(ZeroFormattableAttributeShortName); - if (!isUnion && !isZeroFormattable) + if (!isUnion && zeroFormattableAttribute == null) { return; } @@ -176,8 +184,21 @@ static void Analyze(SyntaxNodeAnalysisContext context) { VerifyUnion(reportContext, typeDeclaration.GetLocation(), declaredSymbol); } - if (isZeroFormattable) + if (zeroFormattableAttribute != null) { + var explicitFormatter = + zeroFormattableAttribute.NamedArguments.Where( + x => x.Key == ZeroFormattableFormatterProperty); + if (explicitFormatter.Any()) + { + if (explicitFormatter.First().Value.Type.BaseType.Name != FormatterTypeName) + reportContext.Add(Diagnostic.Create(ExplicitFormatterMustInheritFormatter, + typeDeclaration.GetLocation(), typeDeclaration.GetLocation(), + explicitFormatter.First().Value.Type)); + + return; + } + VerifyType(reportContext, typeDeclaration.GetLocation(), declaredSymbol, new HashSet(), null); } diff --git a/src/ZeroFormatter.Interfaces/Attributes.cs b/src/ZeroFormatter.Interfaces/Attributes.cs index 292a297..ca37374 100644 --- a/src/ZeroFormatter.Interfaces/Attributes.cs +++ b/src/ZeroFormatter.Interfaces/Attributes.cs @@ -5,6 +5,7 @@ namespace ZeroFormatter [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = false, Inherited = true)] public class ZeroFormattableAttribute : Attribute { + public Type FormatterType { get; set; } } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false, Inherited = true)] diff --git a/src/ZeroFormatter/Formatters/Formatter.cs b/src/ZeroFormatter/Formatters/Formatter.cs index def65ad..23053ef 100644 --- a/src/ZeroFormatter/Formatters/Formatter.cs +++ b/src/ZeroFormatter/Formatters/Formatter.cs @@ -105,7 +105,33 @@ static Formatter() var resolver = ResolverCache.Default; try { - formatter = resolver.ResolveFormatter(t); + +#if NET_CORE + var ti = t.GetTypeInfo(); +#else + var ti = t; +#endif + var attribute = (ZeroFormattableAttribute) ti.GetCustomAttributes(typeof(ZeroFormattableAttribute)) + .FirstOrDefault(); + if (attribute != null && attribute.FormatterType != null) + { + Type[] genericArguments; + if (ti.IsGenericType) + { + var classTypes = ti.GetGenericArguments(); + genericArguments = new Type[1 + classTypes.Length]; + genericArguments[0] = typeof(TTypeResolver); + Array.Copy(classTypes, 0, genericArguments, 1, classTypes.Length); + } + else + genericArguments = new[] {typeof(TTypeResolver)}; + + formatter = Activator.CreateInstance( + attribute.FormatterType.MakeGenericType(genericArguments)); + } + + if (formatter == null) + formatter = resolver.ResolveFormatter(t); // If Unity, should avoid long static constructor and code because IL2CPP generate every . if (formatter == null && resolver.IsUseBuiltinSerializer) diff --git a/tests/ZeroFormatter.Tests/ZeroFormatter.Tests.csproj b/tests/ZeroFormatter.Tests/ZeroFormatter.Tests.csproj index 1e5d935..e79c50a 100644 --- a/tests/ZeroFormatter.Tests/ZeroFormatter.Tests.csproj +++ b/tests/ZeroFormatter.Tests/ZeroFormatter.Tests.csproj @@ -97,6 +97,7 @@ + diff --git a/tests/ZeroFormatter.Tests/ZeroFormatterAttributeTest.cs b/tests/ZeroFormatter.Tests/ZeroFormatterAttributeTest.cs new file mode 100644 index 0000000..dc8531c --- /dev/null +++ b/tests/ZeroFormatter.Tests/ZeroFormatterAttributeTest.cs @@ -0,0 +1,55 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using ZeroFormatter.Formatters; +using ZeroFormatter.Internal; + +namespace ZeroFormatter.Tests +{ + [TestClass] + public class ZeroFormatterAttributeTest + { + [TestMethod] + public void Serialize() + { + var customClass = new CustomClass("Hello World"); + + var data = ZeroFormatterSerializer.Serialize(customClass); + var result = ZeroFormatterSerializer.Deserialize(data); + result.Test.Is(customClass.Test); + } + + [ZeroFormattable(FormatterType = typeof(CustomClassFormatter<>))] + public class CustomClass + { + public CustomClass(string test) + { + Test = test; + } + + public string Test { get; } + } + + public class CustomClassFormatter : Formatter + where TTypeResolver : ITypeResolver, new() + { + public override int? GetLength() + { + return null; + } + + public override int Serialize(ref byte[] bytes, int offset, CustomClass value) + { + var stringLength = BinaryUtil.WriteString(ref bytes, offset + 4, value.Test); + BinaryUtil.WriteInt32Unsafe(ref bytes, offset, stringLength); + return stringLength + 4; + } + + public override CustomClass Deserialize(ref byte[] bytes, int offset, DirtyTracker tracker, + out int byteSize) + { + var length = BinaryUtil.ReadInt32(ref bytes, offset); + byteSize = 4 + length; + return new CustomClass(BinaryUtil.ReadString(ref bytes, offset + 4, length)); + } + } + } +} \ No newline at end of file From 38d392f2b7af92bf54a137c35bb3084e1a77a997 Mon Sep 17 00:00:00 2001 From: "Anapher (Vincent)" Date: Mon, 4 Sep 2017 18:38:27 +0200 Subject: [PATCH 2/2] Update package references --- .../ZeroFormatter.Interfaces.NETCore.csproj | 4 +-- .../ZeroFormatter.NETCore.csproj | 28 +++++++++---------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/ZeroFormatter.Interfaces.NETCore/ZeroFormatter.Interfaces.NETCore.csproj b/src/ZeroFormatter.Interfaces.NETCore/ZeroFormatter.Interfaces.NETCore.csproj index e6d4650..f630139 100644 --- a/src/ZeroFormatter.Interfaces.NETCore/ZeroFormatter.Interfaces.NETCore.csproj +++ b/src/ZeroFormatter.Interfaces.NETCore/ZeroFormatter.Interfaces.NETCore.csproj @@ -12,8 +12,8 @@ - - + + diff --git a/src/ZeroFormatter.NETCore/ZeroFormatter.NETCore.csproj b/src/ZeroFormatter.NETCore/ZeroFormatter.NETCore.csproj index c67c127..5188a41 100644 --- a/src/ZeroFormatter.NETCore/ZeroFormatter.NETCore.csproj +++ b/src/ZeroFormatter.NETCore/ZeroFormatter.NETCore.csproj @@ -26,20 +26,20 @@ - - - - - - - - - - - - - - + + + + + + + + + + + + + +