From faa9a7c54441c3a6aba78e52afd6ce64e54c4c35 Mon Sep 17 00:00:00 2001 From: neuecc Date: Tue, 10 Oct 2017 02:45:07 +0900 Subject: [PATCH] complete support MessagePackFormatterAtribute per member --- sandbox/DynamicCodeDumper/Program.cs | 41 +++++- .../Resolvers/DynamicObjectResolver.cs | 120 ++++++++++-------- .../MessagePack.Tests.csproj | 1 + .../MessagePackFormatterPerField.cs | 98 ++++++++++++++ 4 files changed, 204 insertions(+), 56 deletions(-) create mode 100644 tests/MessagePack.Tests/MessagePackFormatterPerField.cs diff --git a/sandbox/DynamicCodeDumper/Program.cs b/sandbox/DynamicCodeDumper/Program.cs index c76315cc..d0d1cde4 100644 --- a/sandbox/DynamicCodeDumper/Program.cs +++ b/sandbox/DynamicCodeDumper/Program.cs @@ -1,4 +1,5 @@ using MessagePack; +using MessagePack.Formatters; using MessagePack.Internal; using MessagePack.Resolvers; using SharedData; @@ -33,7 +34,7 @@ namespace DynamicCodeDumper //DynamicObjectResolver.Instance.GetFormatter(); //DynamicObjectResolver.Instance.GetFormatter(); //DynamicObjectResolver.Instance.GetFormatter(); - var f = DynamicObjectResolver.Instance.GetFormatter(); + var f = DynamicObjectResolver.Instance.GetFormatter(); //DynamicObjectResolver.Instance.GetFormatter(); //DynamicObjectResolver.Instance.GetFormatter(); //DynamicObjectResolver.Instance.GetFormatter(); @@ -55,7 +56,7 @@ namespace DynamicCodeDumper //DynamicContractlessObjectResolver.Instance.GetFormatter(); byte[] b = null; - f.Serialize(ref b, 0, default(Callback2_2), null); + f.Serialize(ref b, 0, new MyClass { MyProperty1 = 100, MyProperty2 = "foo" }, null); } catch (Exception ex) @@ -95,6 +96,42 @@ namespace DynamicCodeDumper } } } + [MessagePackObject] + public class MyClass + { + [Key(0)] + [MessagePackFormatter(typeof(Int_x10Formatter))] + public int MyProperty1 { get; set; } + [Key(1)] + [MessagePackFormatter(typeof(String_x2Formatter))] + public string MyProperty2 { get; set; } + } + public class Int_x10Formatter : IMessagePackFormatter + { + public int Deserialize(byte[] bytes, int offset, IFormatterResolver formatterResolver, out int readSize) + { + return MessagePackBinary.ReadInt32(bytes, offset, out readSize) * 10; + } + + public int Serialize(ref byte[] bytes, int offset, int value, IFormatterResolver formatterResolver) + { + return MessagePackBinary.WriteInt32(ref bytes, offset, value * 10); + } + } + + public class String_x2Formatter : IMessagePackFormatter + { + public string Deserialize(byte[] bytes, int offset, IFormatterResolver formatterResolver, out int readSize) + { + var s = MessagePackBinary.ReadString(bytes, offset, out readSize); + return s + s; + } + + public int Serialize(ref byte[] bytes, int offset, string value, IFormatterResolver formatterResolver) + { + return MessagePackBinary.WriteString(ref bytes, offset, value + value); + } + } [MessagePackObject(true)] public struct Callback2_2 : IMessagePackSerializationCallbackReceiver diff --git a/src/MessagePack/Resolvers/DynamicObjectResolver.cs b/src/MessagePack/Resolvers/DynamicObjectResolver.cs index fbea3380..cf6e84ad 100644 --- a/src/MessagePack/Resolvers/DynamicObjectResolver.cs +++ b/src/MessagePack/Resolvers/DynamicObjectResolver.cs @@ -249,11 +249,13 @@ namespace MessagePack.Internal }, (index, member) => { FieldInfo fi; - if (!customFormatterLookup.TryGetValue(member, out fi)) return false; + if (!customFormatterLookup.TryGetValue(member, out fi)) return null; - il.EmitLoadThis(); - il.EmitLdfld(fi); - return true; + return () => + { + il.EmitLoadThis(); + il.EmitLdfld(fi); + }; }, 1); } @@ -322,14 +324,16 @@ namespace MessagePack.Internal il.EmitLdarg(0); }, (index, member) => { - if (serializeCustomFormatters.Count == 0) return false; - if (serializeCustomFormatters[index] == null) return false; + if (serializeCustomFormatters.Count == 0) return null; + if (serializeCustomFormatters[index] == null) return null; - il.EmitLdarg(1); // read object[] - il.EmitLdc_I4(index); - il.Emit(OpCodes.Ldelem_Ref); // object - il.Emit(OpCodes.Castclass, serializeCustomFormatters[index].GetType()); - return true; + return () => + { + il.EmitLdarg(1); // read object[] + il.EmitLdc_I4(index); + il.Emit(OpCodes.Ldelem_Ref); // object + il.Emit(OpCodes.Castclass, serializeCustomFormatters[index].GetType()); + }; }, 2); // 0, 1 is parameter. } @@ -383,59 +387,55 @@ namespace MessagePack.Internal static Dictionary BuildCustomFormatterField(TypeBuilder builder, ObjectSerializationInfo info, ILGenerator il) { - // TODO Dictionary dict = new Dictionary(); - //foreach (var item in info.Members.Where(x => x.IsReadable || x.IsWritable)) - //{ - // var attr = item.GetMessagePackFormatterAttribtue(); - // if (attr != null) - // { - // // var attr = typeof(Foo).Get .GetCustomAttribute(true); - // // this.f = Activator.CreateInstance(attr.FormatterType, attr.Arguments); + foreach (var item in info.Members.Where(x => x.IsReadable || x.IsWritable)) + { + var attr = item.GetMessagePackFormatterAttribtue(); + if (attr != null) + { + var f = builder.DefineField(item.Name + "_formatter", attr.FormatterType, FieldAttributes.Private | FieldAttributes.InitOnly); - // var f = builder.DefineField(item.Name + "_formatter", attr.FormatterType, FieldAttributes.Private | FieldAttributes.InitOnly); + var bindingFlags = (int)(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); - // // var bindingFlags = (int)(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); + var attrVar = il.DeclareLocal(typeof(MessagePackFormatterAttribute)); - // var attrVar = il.DeclareLocal(typeof(MessagePackFormatterAttribute)); - - // il.Emit(OpCodes.Ldtoken, info.Type); - // il.EmitCall(EmitInfo.GetTypeFromHandle); - // il.Emit(OpCodes.Ldstr, item.Name); - // il.EmitLdc_I4(bindingFlags); - // if (item.IsProperty) - // { - // il.EmitCall(EmitInfo.TypeGetProperty); - // } - // else - // { - // il.EmitCall(EmitInfo.TypeGetField); - // } + il.Emit(OpCodes.Ldtoken, info.Type); + il.EmitCall(EmitInfo.GetTypeFromHandle); + il.Emit(OpCodes.Ldstr, item.Name); + il.EmitLdc_I4(bindingFlags); + if (item.IsProperty) + { + il.EmitCall(EmitInfo.TypeGetProperty); + } + else + { + il.EmitCall(EmitInfo.TypeGetField); + } - // il.EmitTrue(); - // il.EmitCall(EmitInfo.GetCustomAttributeJsonFormatterAttribute); - // il.EmitStloc(attrVar); + il.EmitTrue(); + il.EmitCall(EmitInfo.GetCustomAttributeMessagePackFormatterAttribute); + il.EmitStloc(attrVar); - // il.EmitLoadThis(); + il.EmitLoadThis(); - // il.EmitLdloc(attrVar); - // il.EmitCall(EmitInfo.JsonFormatterAttr.FormatterType); - // il.EmitLdloc(attrVar); - // il.EmitCall(EmitInfo.JsonFormatterAttr.Arguments); - // il.EmitCall(EmitInfo.ActivatorCreateInstance); + il.EmitLdloc(attrVar); + il.EmitCall(EmitInfo.MessagePackFormatterAttr.FormatterType); + il.EmitLdloc(attrVar); + il.EmitCall(EmitInfo.MessagePackFormatterAttr.Arguments); + il.EmitCall(EmitInfo.ActivatorCreateInstance); - // il.Emit(OpCodes.Castclass, attr.FormatterType); - // il.Emit(OpCodes.Stfld, f); + il.Emit(OpCodes.Castclass, attr.FormatterType); + il.Emit(OpCodes.Stfld, f); - // dict.Add(item, f); - // } - //} + dict.Add(item, f); + } + } return dict; } // int Serialize([arg:1]ref byte[] bytes, [arg:2]int offset, [arg:3]T value, [arg:4]IFormatterResolver formatterResolver); - static void BuildSerialize(Type type, ObjectSerializationInfo info, ILGenerator il, Action emitStringByteKeys, Func tryEmitLoadCustomFormatter, int firstArgIndex) + static void BuildSerialize(Type type, ObjectSerializationInfo info, ILGenerator il, Action emitStringByteKeys, Func tryEmitLoadCustomFormatter, int firstArgIndex) { var argBytes = new ArgumentField(il, firstArgIndex); var argOffset = new ArgumentField(il, firstArgIndex + 1); @@ -596,19 +596,23 @@ namespace MessagePack.Internal argOffset.EmitStore(); } - static void EmitSerializeValue(ILGenerator il, TypeInfo type, ObjectSerializationInfo.EmittableMember member, int index, Func tryEmitLoadCustomFormatter, ArgumentField argBytes, ArgumentField argOffset, ArgumentField argValue, ArgumentField argResolver) + static void EmitSerializeValue(ILGenerator il, TypeInfo type, ObjectSerializationInfo.EmittableMember member, int index, Func tryEmitLoadCustomFormatter, ArgumentField argBytes, ArgumentField argOffset, ArgumentField argValue, ArgumentField argResolver) { var t = member.Type; - if (tryEmitLoadCustomFormatter(index, member)) + var emitter = tryEmitLoadCustomFormatter(index, member); + if (emitter != null) { EmitOffsetPlusEqual(il, () => { + emitter(); }, () => { argValue.EmitLoad(); member.EmitLoadValue(il); argResolver.EmitLoad(); il.EmitCall(typeof(IMessagePackFormatter<>).MakeGenericType(t).GetRuntimeMethod("Serialize", new[] { refByte, typeof(int), t, typeof(IFormatterResolver) })); + + }, argBytes, argOffset); } else if (IsOptimizeTargetType(t)) @@ -1110,8 +1114,16 @@ typeof(int), typeof(int) }); internal static class EmitInfo { public static readonly MethodInfo GetTypeFromHandle = ExpressionUtility.GetMethodInfo(() => Type.GetTypeFromHandle(default(RuntimeTypeHandle))); - //public static readonly MethodInfo TypeGetProperty = ExpressionUtility.GetMethodInfo((Type t) => t.GetProperty(default(string), default(BindingFlags))); - //public static readonly MethodInfo TypeGetField = ExpressionUtility.GetMethodInfo((Type t) => t.GetField(default(string), default(BindingFlags))); + public static readonly MethodInfo TypeGetProperty = ExpressionUtility.GetMethodInfo((Type t) => t.GetProperty(default(string), default(BindingFlags))); + public static readonly MethodInfo TypeGetField = ExpressionUtility.GetMethodInfo((Type t) => t.GetField(default(string), default(BindingFlags))); + public static readonly MethodInfo GetCustomAttributeMessagePackFormatterAttribute = ExpressionUtility.GetMethodInfo(() => CustomAttributeExtensions.GetCustomAttribute(default(MemberInfo), default(bool))); + public static readonly MethodInfo ActivatorCreateInstance = ExpressionUtility.GetMethodInfo(() => Activator.CreateInstance(default(Type), default(object[]))); + + internal static class MessagePackFormatterAttr + { + internal static readonly MethodInfo FormatterType = ExpressionUtility.GetPropertyInfo((MessagePackFormatterAttribute attr) => attr.FormatterType).GetGetMethod(); + internal static readonly MethodInfo Arguments = ExpressionUtility.GetPropertyInfo((MessagePackFormatterAttribute attr) => attr.Arguments).GetGetMethod(); + } } class DeserializeInfo diff --git a/tests/MessagePack.Tests/MessagePack.Tests.csproj b/tests/MessagePack.Tests/MessagePack.Tests.csproj index 0a55aa71..acab696b 100644 --- a/tests/MessagePack.Tests/MessagePack.Tests.csproj +++ b/tests/MessagePack.Tests/MessagePack.Tests.csproj @@ -109,6 +109,7 @@ + diff --git a/tests/MessagePack.Tests/MessagePackFormatterPerField.cs b/tests/MessagePack.Tests/MessagePackFormatterPerField.cs new file mode 100644 index 00000000..40cc2eed --- /dev/null +++ b/tests/MessagePack.Tests/MessagePackFormatterPerField.cs @@ -0,0 +1,98 @@ +using MessagePack.Formatters; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Xunit; + +namespace MessagePack.Tests +{ + public class MessagePackFormatterPerFieldTest + { + [MessagePackObject] + public class MyClass + { + [Key(0)] + [MessagePackFormatter(typeof(Int_x10Formatter))] + public int MyProperty1 { get; set; } + [Key(1)] + public int MyProperty2 { get; set; } + [Key(2)] + [MessagePackFormatter(typeof(String_x2Formatter))] + public string MyProperty3 { get; set; } + [Key(3)] + public string MyProperty4 { get; set; } + } + + [MessagePackObject] + public struct MyStruct + { + [Key(0)] + [MessagePackFormatter(typeof(Int_x10Formatter))] + public int MyProperty1 { get; set; } + [Key(1)] + public int MyProperty2 { get; set; } + [Key(2)] + [MessagePackFormatter(typeof(String_x2Formatter))] + public string MyProperty3 { get; set; } + [Key(3)] + public string MyProperty4 { get; set; } + } + + public class Int_x10Formatter : IMessagePackFormatter + { + public int Deserialize(byte[] bytes, int offset, IFormatterResolver formatterResolver, out int readSize) + { + return MessagePackBinary.ReadInt32(bytes, offset, out readSize) * 10; + } + + public int Serialize(ref byte[] bytes, int offset, int value, IFormatterResolver formatterResolver) + { + return MessagePackBinary.WriteInt32(ref bytes, offset, value * 10); + } + } + + public class String_x2Formatter : IMessagePackFormatter + { + public string Deserialize(byte[] bytes, int offset, IFormatterResolver formatterResolver, out int readSize) + { + var s = MessagePackBinary.ReadString(bytes, offset, out readSize); + return s + s; + } + + public int Serialize(ref byte[] bytes, int offset, string value, IFormatterResolver formatterResolver) + { + return MessagePackBinary.WriteString(ref bytes, offset, value + value); + } + } + + + [Fact] + public void FooBar() + { + { + var bin = MessagePack.MessagePackSerializer.Serialize(new MyClass { MyProperty1 = 100, MyProperty2 = 9, MyProperty3 = "foo", MyProperty4 = "bar" }); + var json = MessagePackSerializer.ToJson(bin); + json.Is("[1000,9,\"foofoo\",\"bar\"]"); + + var r2 = MessagePackSerializer.Deserialize(bin); + r2.MyProperty1.Is(10000); + r2.MyProperty2.Is(9); + r2.MyProperty3.Is("foofoofoofoo"); + r2.MyProperty4.Is("bar"); + } + { + var bin = MessagePack.MessagePackSerializer.Serialize(new MyStruct { MyProperty1 = 100, MyProperty2 = 9, MyProperty3 = "foo", MyProperty4 = "bar" }); + var json = MessagePackSerializer.ToJson(bin); + json.Is("[1000,9,\"foofoo\",\"bar\"]"); + + var r2 = MessagePackSerializer.Deserialize(bin); + r2.MyProperty1.Is(10000); + r2.MyProperty2.Is(9); + r2.MyProperty3.Is("foofoofoofoo"); + r2.MyProperty4.Is("bar"); + } + } + } +}