add Member Custom Serialize(WIP, not full implemented and tests) #123
This commit is contained in:
Родитель
45630d97bf
Коммит
5508adc2b2
|
@ -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)
|
||||
|
|
Загрузка…
Ссылка в новой задаче