add Member Custom Serialize(WIP, not full implemented and tests) #123

This commit is contained in:
neuecc 2017-09-05 19:21:58 +09:00
Родитель 45630d97bf
Коммит 5508adc2b2
6 изменённых файлов: 310 добавлений и 15 удалений

Просмотреть файл

@ -77,6 +77,9 @@
<Compile Include="..\..\src\MessagePack\Internal\DynamicAssembly.cs">
<Link>Code\DynamicAssembly.cs</Link>
</Compile>
<Compile Include="..\..\src\MessagePack\Internal\ExpressionUtility.cs">
<Link>Code\ExpressionUtility.cs</Link>
</Compile>
<Compile Include="..\..\src\MessagePack\Internal\FarmHash.cs">
<Link>Code\FarmHash.cs</Link>
</Compile>

Просмотреть файл

@ -29,6 +29,7 @@ namespace SharedData
public class SimpleIntKeyData
{
[Key(0)]
//[MessagePackFormatter(typeof(OreOreFormatter))]
public int Prop1 { get; set; }
[Key(1)]
public ByteEnum Prop2 { get; set; }
@ -42,6 +43,41 @@ namespace SharedData
public SimpleStructStringKeyData Prop6 { get; set; }
[Key(6)]
public byte[] BytesSpecial { get; set; }
//[Key(7)]
//[MessagePackFormatter(typeof(OreOreFormatter2), 100, "hogehoge")]
//[MessagePackFormatter(typeof(OreOreFormatter))]
//public int Prop7 { get; set; }
}
public class OreOreFormatter : IMessagePackFormatter<int>
{
public int Deserialize(byte[] bytes, int offset, IFormatterResolver formatterResolver, out int readSize)
{
throw new NotImplementedException();
}
public int Serialize(ref byte[] bytes, int offset, int value, IFormatterResolver formatterResolver)
{
throw new NotImplementedException();
}
}
public class OreOreFormatter2 : IMessagePackFormatter<int>
{
public OreOreFormatter2(int x, string y)
{
}
public int Deserialize(byte[] bytes, int offset, IFormatterResolver formatterResolver, out int readSize)
{
throw new NotImplementedException();
}
public int Serialize(ref byte[] bytes, int offset, int value, IFormatterResolver formatterResolver)
{
throw new NotImplementedException();
}
}
[MessagePackObject(true)]

Просмотреть файл

@ -54,7 +54,7 @@ namespace MessagePack
}
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface | AttributeTargets.Enum, AllowMultiple = false, Inherited = true)]
[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; }

Просмотреть файл

@ -0,0 +1,88 @@
using System;
using System.Linq.Expressions;
using System.Reflection;
namespace MessagePack.Internal
{
public static class ExpressionUtility
{
// Method
static MethodInfo GetMethodInfoCore(LambdaExpression expression)
{
if (expression == null)
{
throw new ArgumentNullException("expression");
}
return (expression.Body as MethodCallExpression).Method;
}
/// <summary>
/// Get MethodInfo from Expression for Static(with result) method.
/// </summary>
public static MethodInfo GetMethodInfo<T>(Expression<Func<T>> expression)
{
return GetMethodInfoCore(expression);
}
/// <summary>
/// Get MethodInfo from Expression for Static(void) method.
/// </summary>
public static MethodInfo GetMethodInfo(Expression<Action> expression)
{
return GetMethodInfoCore(expression);
}
/// <summary>
/// Get MethodInfo from Expression for Instance(with result) method.
/// </summary>
public static MethodInfo GetMethodInfo<T, TR>(Expression<Func<T, TR>> expression)
{
return GetMethodInfoCore(expression);
}
/// <summary>
/// Get MethodInfo from Expression for Instance(void) method.
/// </summary>
public static MethodInfo GetMethodInfo<T>(Expression<Action<T>> expression)
{
return GetMethodInfoCore(expression);
}
// WithArgument(for ref, out) helper
/// <summary>
/// Get MethodInfo from Expression for Instance(with result) method.
/// </summary>
public static MethodInfo GetMethodInfo<T, TArg1, TR>(Expression<Func<T, TArg1, TR>> expression)
{
return GetMethodInfoCore(expression);
}
// Property
static MemberInfo GetMemberInfoCore<T>(Expression<T> source)
{
if (source == null)
{
throw new ArgumentNullException("source");
}
var memberExpression = source.Body as MemberExpression;
return memberExpression.Member;
}
public static PropertyInfo GetPropertyInfo<T, TR>(Expression<Func<T, TR>> expression)
{
return GetMemberInfoCore(expression) as PropertyInfo;
}
// Field
public static FieldInfo GetFieldInfo<T, TR>(Expression<Func<T, TR>> expression)
{
return GetMemberInfoCore(expression) as FieldInfo;
}
}
}

Просмотреть файл

@ -281,6 +281,63 @@ namespace MessagePack.Internal
il.Emit(OpCodes.Ldc_I8, unchecked((long)value));
}
// bool、byte、char、double、float、int、long、short、string
// object, type, enum, or its array
public static void EmitConstant(this ILGenerator il, object value)
{
if (value == null) return;
if (value is string)
{
il.Emit(OpCodes.Ldstr, (string)value);
}
else if (value is bool)
{
EmitLdc_I4(il, ((bool)value) ? 1 : 0);
}
else if (value is byte)
{
EmitLdc_I4(il, (int)(byte)value);
}
else if (value is char)
{
EmitLdc_I4(il, (int)(char)value);
}
else if (value is double)
{
il.Emit(OpCodes.Ldc_R8, (double)value);
}
else if (value is float)
{
il.Emit(OpCodes.Ldc_R4, (float)value);
}
else if (value is int)
{
il.Emit(OpCodes.Ldc_I4, (int)value);
}
else if (value is long)
{
il.Emit(OpCodes.Ldc_I8, (long)value);
}
else if (value is short)
{
EmitLdc_I4(il, (int)(short)value);
}
else if (value is Type)
{
// TODO:
throw new NotImplementedException("TODO:Not Implemented");
}
else if (value.GetType().GetTypeInfo().IsEnum)
{
throw new NotImplementedException("TODO:Not Implemented");
}
else if (value.GetType().GetTypeInfo().IsArray)
{
throw new NotImplementedException("TODO:Not Implemented");
}
}
public static void EmitThrowNotimplemented(this ILGenerator il)
{
il.Emit(OpCodes.Newobj, typeof(System.NotImplementedException).GetTypeInfo().DeclaredConstructors.First(x => x.GetParameters().Length == 0));

Просмотреть файл

@ -210,6 +210,7 @@ namespace MessagePack.Internal
var typeBuilder = assembly.ModuleBuilder.DefineType("MessagePack.Formatters." + SubtractFullNameRegex.Replace(type.FullName, "").Replace(".", "_") + "Formatter", TypeAttributes.Public | TypeAttributes.Sealed, null, new[] { formatterType });
FieldBuilder stringByteKeysField = null;
Dictionary<ObjectSerializationInfo.EmittableMember, FieldInfo> dict = null;
// string key needs string->int mapper for deserialize switch statement
if (serializationInfo.IsStringKey)
@ -219,14 +220,26 @@ namespace MessagePack.Internal
var il = method.GetILGenerator();
BuildConstructor(type, serializationInfo, method, stringByteKeysField, il);
dict = BuildCustomFormatterField(typeBuilder, serializationInfo, il);
il.Emit(OpCodes.Ret);
}
else
{
var method = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, Type.EmptyTypes);
var il = method.GetILGenerator();
il.EmitLdarg(0);
il.Emit(OpCodes.Call, objectCtor);
dict = BuildCustomFormatterField(typeBuilder, serializationInfo, il);
il.Emit(OpCodes.Ret);
}
{
var method = typeBuilder.DefineMethod("Serialize", MethodAttributes.Public | MethodAttributes.Final | MethodAttributes.Virtual,
typeof(int),
new Type[] { typeof(byte[]).MakeByRefType(), typeof(int), type, typeof(IFormatterResolver) });
var il = method.GetILGenerator();
BuildSerialize(type, serializationInfo, method, stringByteKeysField, il);
BuildSerialize(type, serializationInfo, method, stringByteKeysField, dict, il);
}
{
@ -235,7 +248,7 @@ namespace MessagePack.Internal
new Type[] { typeof(byte[]), typeof(int), typeof(IFormatterResolver), typeof(int).MakeByRefType() });
var il = method.GetILGenerator();
BuildDeserialize(type, serializationInfo, method, il);
BuildDeserialize(type, serializationInfo, method, il, dict);
}
return typeBuilder.CreateTypeInfo();
@ -263,12 +276,63 @@ namespace MessagePack.Internal
}
il.Emit(OpCodes.Stfld, stringByteKeysField);
}
il.Emit(OpCodes.Ret);
static Dictionary<ObjectSerializationInfo.EmittableMember, FieldInfo> BuildCustomFormatterField(TypeBuilder builder, ObjectSerializationInfo info, ILGenerator il)
{
Dictionary<ObjectSerializationInfo.EmittableMember, FieldInfo> dict = new Dictionary<ObjectSerializationInfo.EmittableMember, FieldInfo>();
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);
il.EmitLdarg(0);
il.Emit(OpCodes.Ldtoken, f.FieldType);
var getTypeFromHandle = ExpressionUtility.GetMethodInfo(() => Type.GetTypeFromHandle(default(RuntimeTypeHandle)));
il.Emit(OpCodes.Call, getTypeFromHandle);
if (attr.Arguments == null || attr.Arguments.Length == 0)
{
var mi = ExpressionUtility.GetMethodInfo(() => Activator.CreateInstance(default(Type)));
il.Emit(OpCodes.Call, mi);
}
else
{
il.EmitLdc_I4(attr.Arguments.Length);
il.Emit(OpCodes.Newarr, typeof(object));
var ii = 0;
foreach (var item2 in attr.Arguments)
{
il.Emit(OpCodes.Dup);
il.EmitLdc_I4(ii);
il.EmitConstant(item2);
if (item2.GetType().GetTypeInfo().IsValueType)
{
il.Emit(OpCodes.Box, item2.GetType());
}
il.Emit(OpCodes.Stelem_Ref);
ii++;
}
var mi = ExpressionUtility.GetMethodInfo(() => Activator.CreateInstance(default(Type), default(object[])));
il.Emit(OpCodes.Call, mi);
}
il.Emit(OpCodes.Castclass, attr.FormatterType);
il.Emit(OpCodes.Stfld, 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, MethodInfo method, FieldBuilder stringByteKeysField, ILGenerator il)
static void BuildSerialize(Type type, ObjectSerializationInfo info, MethodInfo method, FieldBuilder stringByteKeysField, Dictionary<ObjectSerializationInfo.EmittableMember, FieldInfo> customFormatterLookup, ILGenerator il)
{
// if(value == null) return WriteNil
if (type.GetTypeInfo().IsClass)
@ -344,7 +408,7 @@ namespace MessagePack.Internal
if (intKeyMap.TryGetValue(i, out member))
{
// offset += serialzie
EmitSerializeValue(il, type.GetTypeInfo(), member);
EmitSerializeValue(il, type.GetTypeInfo(), member, customFormatterLookup);
}
else
{
@ -408,7 +472,7 @@ namespace MessagePack.Internal
});
// offset += serialzie
EmitSerializeValue(il, type.GetTypeInfo(), item);
EmitSerializeValue(il, type.GetTypeInfo(), item, customFormatterLookup);
}
}
@ -435,10 +499,25 @@ namespace MessagePack.Internal
il.EmitStarg(2);
}
static void EmitSerializeValue(ILGenerator il, TypeInfo type, ObjectSerializationInfo.EmittableMember member)
static void EmitSerializeValue(ILGenerator il, TypeInfo type, ObjectSerializationInfo.EmittableMember member, Dictionary<ObjectSerializationInfo.EmittableMember, FieldInfo> customFormatterLookup)
{
var t = member.Type;
if (IsOptimizeTargetType(t))
FieldInfo customFormatter;
if (customFormatterLookup.TryGetValue(member, out customFormatter))
{
EmitOffsetPlusEqual(il, () =>
{
il.Emit(OpCodes.Ldarg_0);
il.EmitLdfld(customFormatter);
}, () =>
{
il.EmitLoadArg(type, 3);
member.EmitLoadValue(il);
il.EmitLdarg(4);
il.EmitCall(customFormatter.FieldType.GetRuntimeMethod("Serialize", new[] { refByte, typeof(int), t, typeof(IFormatterResolver) }));
});
}
else if (IsOptimizeTargetType(t))
{
EmitOffsetPlusEqual(il, null, () =>
{
@ -471,7 +550,7 @@ namespace MessagePack.Internal
}
// T Deserialize([arg:1]byte[] bytes, [arg:2]int offset, [arg:3]IFormatterResolver formatterResolver, [arg:4]out int readSize);
static void BuildDeserialize(Type type, ObjectSerializationInfo info, MethodBuilder method, ILGenerator il)
static void BuildDeserialize(Type type, ObjectSerializationInfo info, MethodBuilder method, ILGenerator il, Dictionary<ObjectSerializationInfo.EmittableMember, FieldInfo> customFormatterLookup)
{
// if(MessagePackBinary.IsNil) readSize = 1, return null;
var falseLabel = il.DefineLabel();
@ -625,7 +704,7 @@ namespace MessagePack.Internal
var i = x.Value;
if (infoList[i].MemberInfo != null)
{
EmitDeserializeValue(il, infoList[i]);
EmitDeserializeValue(il, infoList[i], customFormatterLookup);
il.Emit(OpCodes.Br, loopEnd);
}
else
@ -690,7 +769,7 @@ namespace MessagePack.Internal
if (item.MemberInfo != null)
{
il.MarkLabel(item.SwitchLabel);
EmitDeserializeValue(il, item);
EmitDeserializeValue(il, item, customFormatterLookup);
il.Emit(OpCodes.Br, loopEnd);
}
}
@ -762,11 +841,22 @@ namespace MessagePack.Internal
il.EmitStarg(2);
}
static void EmitDeserializeValue(ILGenerator il, DeserializeInfo info)
static void EmitDeserializeValue(ILGenerator il, DeserializeInfo info, Dictionary<ObjectSerializationInfo.EmittableMember, FieldInfo> customFormatterLookup)
{
var member = info.MemberInfo;
var t = member.Type;
if (IsOptimizeTargetType(t))
FieldInfo customFormatter;
if (customFormatterLookup.TryGetValue(member, out customFormatter))
{
il.Emit(OpCodes.Ldarg_0);
il.EmitLdfld(customFormatter);
il.EmitLdarg(1);
il.EmitLdarg(2);
il.EmitLdarg(3);
il.EmitLdarg(4);
il.EmitCall(customFormatter.FieldType.GetRuntimeMethod("Deserialize", new[] { typeof(byte[]), typeof(int), typeof(IFormatterResolver), refInt }));
}
else if (IsOptimizeTargetType(t))
{
il.EmitLdarg(1);
il.EmitLdarg(2);
@ -891,7 +981,7 @@ namespace MessagePack.Internal
public static TypeInfo TypeInfo = typeof(MessagePackBinary).GetTypeInfo();
public static readonly MethodInfo GetEncodedStringBytes = typeof(MessagePackBinary).GetRuntimeMethod("GetEncodedStringBytes", new[] { typeof(string) });
public static MethodInfo WriteFixedMapHeaderUnsafe = typeof(MessagePackBinary).GetRuntimeMethod("WriteFixedMapHeaderUnsafe", new[] { refByte,
public static MethodInfo WriteFixedMapHeaderUnsafe = typeof(MessagePackBinary).GetRuntimeMethod("WriteFixedMapHeaderUnsafe", new[] { refByte,
typeof(int), typeof(int) });
public static MethodInfo WriteFixedArrayHeaderUnsafe = typeof(MessagePackBinary).GetRuntimeMethod("WriteFixedArrayHeaderUnsafe", new[] { refByte, typeof(int), typeof(int) });
public static MethodInfo WriteMapHeader = typeof(MessagePackBinary).GetRuntimeMethod("WriteMapHeader", new[] { refByte, typeof(int), typeof(int) });
@ -1520,6 +1610,15 @@ typeof(int), typeof(int) });
public Type Type { get { return IsField ? FieldInfo.FieldType : PropertyInfo.PropertyType; } }
public FieldInfo FieldInfo { get; set; }
public PropertyInfo PropertyInfo { get; set; }
public string Name
{
get
{
return IsProperty ? PropertyInfo.Name : FieldInfo.Name;
}
}
public bool IsValueType
{
get
@ -1529,6 +1628,18 @@ typeof(int), typeof(int) });
}
}
public MessagePackFormatterAttribute GetMessagePackFormatterAttribtue()
{
if (IsProperty)
{
return (MessagePackFormatterAttribute)PropertyInfo.GetCustomAttribute(typeof(MessagePackFormatterAttribute), true);
}
else
{
return (MessagePackFormatterAttribute)FieldInfo.GetCustomAttribute(typeof(MessagePackFormatterAttribute), true);
}
}
public void EmitLoadValue(ILGenerator il)
{
if (IsProperty)