#if !UNITY_WSA #if !NET_STANDARD_2_0 using System; using System.Linq; using MessagePack.Formatters; using MessagePack.Internal; using System.Reflection; using System.Reflection.Emit; using System.Collections.Generic; using System.Text.RegularExpressions; using System.Runtime.Serialization; using System.Text; using System.Threading; namespace MessagePack.Resolvers { /// /// ObjectResolver by dynamic code generation. /// public sealed class DynamicObjectResolver : IFormatterResolver { public static readonly DynamicObjectResolver Instance = new DynamicObjectResolver(); const string ModuleName = "MessagePack.Resolvers.DynamicObjectResolver"; internal static readonly DynamicAssembly assembly; DynamicObjectResolver() { } static DynamicObjectResolver() { assembly = new DynamicAssembly(ModuleName); } #if NET_35 public AssemblyBuilder Save() { return assembly.Save(); } #endif public IMessagePackFormatter GetFormatter() { return FormatterCache.formatter; } static class FormatterCache { public static readonly IMessagePackFormatter formatter; static FormatterCache() { var ti = typeof(T).GetTypeInfo(); if (ti.IsInterface) { return; } if (ti.IsNullable()) { ti = ti.GenericTypeArguments[0].GetTypeInfo(); var innerFormatter = DynamicObjectResolver.Instance.GetFormatterDynamic(ti.AsType()); if (innerFormatter == null) { return; } formatter = (IMessagePackFormatter)Activator.CreateInstance(typeof(StaticNullableFormatter<>).MakeGenericType(ti.AsType()), new object[] { innerFormatter }); return; } if (ti.IsAnonymous()) { formatter = (IMessagePackFormatter)DynamicObjectTypeBuilder.BuildFormatterToDynamicMethod(typeof(T), true, true, false); return; } var formatterTypeInfo = DynamicObjectTypeBuilder.BuildType(assembly, typeof(T), false, false); if (formatterTypeInfo == null) return; formatter = (IMessagePackFormatter)Activator.CreateInstance(formatterTypeInfo.AsType()); } } } /// /// ObjectResolver by dynamic code generation, allow private member. /// public sealed class DynamicObjectResolverAllowPrivate : IFormatterResolver { public static readonly DynamicObjectResolverAllowPrivate Instance = new DynamicObjectResolverAllowPrivate(); DynamicObjectResolverAllowPrivate() { } public IMessagePackFormatter GetFormatter() { return FormatterCache.formatter; } static class FormatterCache { public static readonly IMessagePackFormatter formatter; static FormatterCache() { var ti = typeof(T).GetTypeInfo(); if (ti.IsInterface) { return; } if (ti.IsNullable()) { ti = ti.GenericTypeArguments[0].GetTypeInfo(); var innerFormatter = DynamicObjectResolverAllowPrivate.Instance.GetFormatterDynamic(ti.AsType()); if (innerFormatter == null) { return; } formatter = (IMessagePackFormatter)Activator.CreateInstance(typeof(StaticNullableFormatter<>).MakeGenericType(ti.AsType()), new object[] { innerFormatter }); return; } if (ti.IsAnonymous()) { formatter = (IMessagePackFormatter)DynamicObjectTypeBuilder.BuildFormatterToDynamicMethod(typeof(T), true, true, false); } else { formatter = (IMessagePackFormatter)DynamicObjectTypeBuilder.BuildFormatterToDynamicMethod(typeof(T), false, false, true); } } } } /// /// ObjectResolver by dynamic code generation, no needs MessagePackObject attribute and serialized key as string. /// public sealed class DynamicContractlessObjectResolver : IFormatterResolver { public static readonly DynamicContractlessObjectResolver Instance = new DynamicContractlessObjectResolver(); const string ModuleName = "MessagePack.Resolvers.DynamicContractlessObjectResolver"; static readonly DynamicAssembly assembly; DynamicContractlessObjectResolver() { } static DynamicContractlessObjectResolver() { assembly = new DynamicAssembly(ModuleName); } #if NET_35 public AssemblyBuilder Save() { return assembly.Save(); } #endif public IMessagePackFormatter GetFormatter() { return FormatterCache.formatter; } static class FormatterCache { public static readonly IMessagePackFormatter formatter; static FormatterCache() { if (typeof(T) == typeof(object)) { return; } var ti = typeof(T).GetTypeInfo(); if (ti.IsInterface) { return; } if (ti.IsNullable()) { ti = ti.GenericTypeArguments[0].GetTypeInfo(); var innerFormatter = DynamicContractlessObjectResolver.Instance.GetFormatterDynamic(ti.AsType()); if (innerFormatter == null) { return; } formatter = (IMessagePackFormatter)Activator.CreateInstance(typeof(StaticNullableFormatter<>).MakeGenericType(ti.AsType()), new object[] { innerFormatter }); return; } if (ti.IsAnonymous()) { formatter = (IMessagePackFormatter)DynamicObjectTypeBuilder.BuildFormatterToDynamicMethod(typeof(T), true, true, false); return; } var formatterTypeInfo = DynamicObjectTypeBuilder.BuildType(assembly, typeof(T), true, true); if (formatterTypeInfo == null) return; formatter = (IMessagePackFormatter)Activator.CreateInstance(formatterTypeInfo.AsType()); } } } /// /// ObjectResolver by dynamic code generation, no needs MessagePackObject attribute and serialized key as string, allow private member. /// public sealed class DynamicContractlessObjectResolverAllowPrivate : IFormatterResolver { public static readonly DynamicContractlessObjectResolverAllowPrivate Instance = new DynamicContractlessObjectResolverAllowPrivate(); public IMessagePackFormatter GetFormatter() { return FormatterCache.formatter; } static class FormatterCache { public static readonly IMessagePackFormatter formatter; static FormatterCache() { if (typeof(T) == typeof(object)) { return; } var ti = typeof(T).GetTypeInfo(); if (ti.IsInterface) { return; } if (ti.IsNullable()) { ti = ti.GenericTypeArguments[0].GetTypeInfo(); var innerFormatter = DynamicContractlessObjectResolverAllowPrivate.Instance.GetFormatterDynamic(ti.AsType()); if (innerFormatter == null) { return; } formatter = (IMessagePackFormatter)Activator.CreateInstance(typeof(StaticNullableFormatter<>).MakeGenericType(ti.AsType()), new object[] { innerFormatter }); return; } if (ti.IsAnonymous()) { formatter = (IMessagePackFormatter)DynamicObjectTypeBuilder.BuildFormatterToDynamicMethod(typeof(T), true, true, false); } else { formatter = (IMessagePackFormatter)DynamicObjectTypeBuilder.BuildFormatterToDynamicMethod(typeof(T), true, true, true); } } } } } namespace MessagePack.Internal { internal static class DynamicObjectTypeBuilder { #if NETSTANDARD static readonly Regex SubtractFullNameRegex = new Regex(@", Version=\d+.\d+.\d+.\d+, Culture=\w+, PublicKeyToken=\w+", RegexOptions.Compiled); #else static readonly Regex SubtractFullNameRegex = new Regex(@", Version=\d+.\d+.\d+.\d+, Culture=\w+, PublicKeyToken=\w+"); #endif static int nameSequence = 0; static HashSet ignoreTypes = new HashSet { {typeof(object)}, {typeof(short)}, {typeof(int)}, {typeof(long)}, {typeof(ushort)}, {typeof(uint)}, {typeof(ulong)}, {typeof(float)}, {typeof(double)}, {typeof(bool)}, {typeof(byte)}, {typeof(sbyte)}, {typeof(decimal)}, {typeof(char)}, {typeof(string)}, {typeof(System.Guid)}, {typeof(System.TimeSpan)}, {typeof(System.DateTime)}, {typeof(System.DateTimeOffset)}, {typeof(MessagePack.Nil)}, }; public static TypeInfo BuildType(DynamicAssembly assembly, Type type, bool forceStringKey, bool contractless) { if (ignoreTypes.Contains(type)) return null; var serializationInfo = MessagePack.Internal.ObjectSerializationInfo.CreateOrNull(type, forceStringKey, contractless, false); if (serializationInfo == null) return null; var formatterType = typeof(IMessagePackFormatter<>).MakeGenericType(type); var typeBuilder = assembly.DefineType("MessagePack.Formatters." + SubtractFullNameRegex.Replace(type.FullName, "").Replace(".", "_") + "Formatter" + Interlocked.Increment(ref nameSequence), TypeAttributes.Public | TypeAttributes.Sealed, null, new[] { formatterType }); FieldBuilder stringByteKeysField = null; Dictionary customFormatterLookup = null; // string key needs string->int mapper for deserialize switch statement if (serializationInfo.IsStringKey) { var method = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, Type.EmptyTypes); stringByteKeysField = typeBuilder.DefineField("stringByteKeys", typeof(byte[][]), FieldAttributes.Private | FieldAttributes.InitOnly); var il = method.GetILGenerator(); BuildConstructor(type, serializationInfo, method, stringByteKeysField, il); customFormatterLookup = BuildCustomFormatterField(typeBuilder, serializationInfo, il); il.Emit(OpCodes.Ret); } else { var method = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, Type.EmptyTypes); var il = method.GetILGenerator(); il.EmitLoadThis(); il.Emit(OpCodes.Call, objectCtor); customFormatterLookup = 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, il, () => { il.EmitLoadThis(); il.EmitLdfld(stringByteKeysField); }, (index, member) => { FieldInfo fi; if (!customFormatterLookup.TryGetValue(member, out fi)) return null; return () => { il.EmitLoadThis(); il.EmitLdfld(fi); }; }, 1); } { var method = typeBuilder.DefineMethod("Deserialize", MethodAttributes.Public | MethodAttributes.Final | MethodAttributes.Virtual, type, new Type[] { typeof(byte[]), typeof(int), typeof(IFormatterResolver), typeof(int).MakeByRefType() }); var il = method.GetILGenerator(); BuildDeserialize(type, serializationInfo, il, (index, member) => { FieldInfo fi; if (!customFormatterLookup.TryGetValue(member, out fi)) return null; return () => { il.EmitLoadThis(); il.EmitLdfld(fi); }; }, 1); // firstArgIndex:0 is this. } return typeBuilder.CreateTypeInfo(); } public static object BuildFormatterToDynamicMethod(Type type, bool forceStringKey, bool contractless, bool allowPrivate) { var serializationInfo = ObjectSerializationInfo.CreateOrNull(type, forceStringKey, contractless, allowPrivate); if (serializationInfo == null) return null; // internal delegate int AnonymousSerializeFunc(byte[][] stringByteKeysField, object[] customFormatters, ref byte[] bytes, int offset, T value, IFormatterResolver resolver); // internal delegate T AnonymousDeserializeFunc(object[] customFormatters, byte[] bytes, int offset, IFormatterResolver resolver, out int readSize); var serialize = new DynamicMethod("Serialize", typeof(int), new[] { typeof(byte[][]), typeof(object[]), typeof(byte[]).MakeByRefType(), typeof(int), type, typeof(IFormatterResolver) }, type, true); DynamicMethod deserialize = null; List stringByteKeysField = new List(); List serializeCustomFormatters = new List(); List deserializeCustomFormatters = new List(); if (serializationInfo.IsStringKey) { var i = 0; foreach (var item in serializationInfo.Members.Where(x => x.IsReadable)) { stringByteKeysField.Add(MessagePackBinary.GetEncodedStringBytes(item.StringKey)); i++; } } foreach (var item in serializationInfo.Members.Where(x => x.IsReadable)) { var attr = item.GetMessagePackFormatterAttribtue(); if (attr != null) { var formatter = Activator.CreateInstance(attr.FormatterType, attr.Arguments); serializeCustomFormatters.Add(formatter); } else { serializeCustomFormatters.Add(null); } } foreach (var item in serializationInfo.Members) // not only for writable because for use ctor. { var attr = item.GetMessagePackFormatterAttribtue(); if (attr != null) { var formatter = Activator.CreateInstance(attr.FormatterType, attr.Arguments); deserializeCustomFormatters.Add(formatter); } else { deserializeCustomFormatters.Add(null); } } { var il = serialize.GetILGenerator(); BuildSerialize(type, serializationInfo, il, () => { il.EmitLdarg(0); }, (index, member) => { if (serializeCustomFormatters.Count == 0) return null; if (serializeCustomFormatters[index] == null) return null; 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. } if (serializationInfo.IsStruct || serializationInfo.BestmatchConstructor != null) { deserialize = new DynamicMethod("Deserialize", type, new[] { typeof(object[]), typeof(byte[]), typeof(int), typeof(IFormatterResolver), typeof(int).MakeByRefType() }, type, true); var il = deserialize.GetILGenerator(); BuildDeserialize(type, serializationInfo, il, (index, member) => { if (deserializeCustomFormatters.Count == 0) return null; if (deserializeCustomFormatters[index] == null) return null; return () => { il.EmitLdarg(0); // read object[] il.EmitLdc_I4(index); il.Emit(OpCodes.Ldelem_Ref); // object il.Emit(OpCodes.Castclass, deserializeCustomFormatters[index].GetType()); }; }, 1); } object serializeDelegate = serialize.CreateDelegate(typeof(AnonymousSerializeFunc<>).MakeGenericType(type)); object deserializeDelegate = (deserialize == null) ? (object)null : (object)deserialize.CreateDelegate(typeof(AnonymousDeserializeFunc<>).MakeGenericType(type)); var resultFormatter = Activator.CreateInstance(typeof(AnonymousSerializableFormatter<>).MakeGenericType(type), new[] { stringByteKeysField.ToArray(), serializeCustomFormatters.ToArray(), deserializeCustomFormatters.ToArray(), serializeDelegate, deserializeDelegate }); return resultFormatter; } static void BuildConstructor(Type type, ObjectSerializationInfo info, ConstructorInfo method, FieldBuilder stringByteKeysField, ILGenerator il) { il.EmitLoadThis(); il.Emit(OpCodes.Call, objectCtor); var writeCount = info.Members.Count(x => x.IsReadable); il.EmitLoadThis(); il.EmitLdc_I4(writeCount); il.Emit(OpCodes.Newarr, typeof(byte[])); var i = 0; foreach (var item in info.Members.Where(x => x.IsReadable)) { il.Emit(OpCodes.Dup); il.EmitLdc_I4(i); il.Emit(OpCodes.Ldstr, item.StringKey); il.EmitCall(MessagePackBinaryTypeInfo.GetEncodedStringBytes); il.Emit(OpCodes.Stelem_Ref); i++; } il.Emit(OpCodes.Stfld, stringByteKeysField); } static Dictionary BuildCustomFormatterField(TypeBuilder builder, ObjectSerializationInfo info, ILGenerator il) { Dictionary dict = new Dictionary(); 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 bindingFlags = (int)(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); 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.EmitTrue(); il.EmitCall(EmitInfo.GetCustomAttributeMessagePackFormatterAttribute); il.EmitStloc(attrVar); il.EmitLoadThis(); 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); 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) { var argBytes = new ArgumentField(il, firstArgIndex); var argOffset = new ArgumentField(il, firstArgIndex + 1); var argValue = new ArgumentField(il, firstArgIndex + 2, type); var argResolver = new ArgumentField(il, firstArgIndex + 3); // if(value == null) return WriteNil if (type.GetTypeInfo().IsClass) { var elseBody = il.DefineLabel(); argValue.EmitLoad(); il.Emit(OpCodes.Brtrue_S, elseBody); argBytes.EmitLoad(); argOffset.EmitLoad(); il.EmitCall(MessagePackBinaryTypeInfo.WriteNil); il.Emit(OpCodes.Ret); il.MarkLabel(elseBody); } // IMessagePackSerializationCallbackReceiver.OnBeforeSerialize() if (type.GetTypeInfo().ImplementedInterfaces.Any(x => x == typeof(IMessagePackSerializationCallbackReceiver))) { // call directly var runtimeMethods = type.GetRuntimeMethods().Where(x => x.Name == "OnBeforeSerialize").ToArray(); if (runtimeMethods.Length == 1) { argValue.EmitLoad(); il.Emit(OpCodes.Call, runtimeMethods[0]); // don't use EmitCall helper(must use 'Call') } else { argValue.EmitLdarg(); // force ldarg il.EmitBoxOrDoNothing(type); il.EmitCall(onBeforeSerialize); } } // var startOffset = offset; var startOffsetLocal = il.DeclareLocal(typeof(int)); // [loc:0] argOffset.EmitLoad(); il.EmitStloc(startOffsetLocal); if (info.IsIntKey) { // use Array var maxKey = info.Members.Where(x => x.IsReadable).Select(x => x.IntKey).DefaultIfEmpty(-1).Max(); var intKeyMap = info.Members.Where(x => x.IsReadable).ToDictionary(x => x.IntKey); EmitOffsetPlusEqual(il, null, () => { var len = maxKey + 1; il.EmitLdc_I4(len); if (len <= MessagePackRange.MaxFixArrayCount) { il.EmitCall(MessagePackBinaryTypeInfo.WriteFixedArrayHeaderUnsafe); } else { il.EmitCall(MessagePackBinaryTypeInfo.WriteArrayHeader); } }, argBytes, argOffset); for (int i = 0; i <= maxKey; i++) { ObjectSerializationInfo.EmittableMember member; if (intKeyMap.TryGetValue(i, out member)) { // offset += serialzie EmitSerializeValue(il, type.GetTypeInfo(), member, i, tryEmitLoadCustomFormatter, argBytes, argOffset, argValue, argResolver); } else { // Write Nil as Blanc EmitOffsetPlusEqual(il, null, () => { il.EmitCall(MessagePackBinaryTypeInfo.WriteNil); }, argBytes, argOffset); } } } else { // use Map var writeCount = info.Members.Count(x => x.IsReadable); EmitOffsetPlusEqual(il, null, () => { il.EmitLdc_I4(writeCount); if (writeCount <= MessagePackRange.MaxFixMapCount) { il.EmitCall(MessagePackBinaryTypeInfo.WriteFixedMapHeaderUnsafe); } else { il.EmitCall(MessagePackBinaryTypeInfo.WriteMapHeader); } }, argBytes, argOffset); var index = 0; foreach (var item in info.Members.Where(x => x.IsReadable)) { // offset += writekey EmitOffsetPlusEqual(il, null, () => { emitStringByteKeys(); il.EmitLdc_I4(index); il.Emit(OpCodes.Ldelem_Ref); // Optimize, WriteRaw(Unity, large) or UnsafeMemory32/64.WriteRawX #if NETSTANDARD var valueLen = MessagePackBinary.GetEncodedStringBytes(item.StringKey).Length; if (valueLen <= MessagePackRange.MaxFixStringLength) { if (UnsafeMemory.Is32Bit) { il.EmitCall(typeof(UnsafeMemory32).GetRuntimeMethod("WriteRaw" + valueLen, new[] { refByte, typeof(int), typeof(byte[]) })); } else { il.EmitCall(typeof(UnsafeMemory64).GetRuntimeMethod("WriteRaw" + valueLen, new[] { refByte, typeof(int), typeof(byte[]) })); } } else #endif { il.EmitCall(MessagePackBinaryTypeInfo.WriteRaw); } }, argBytes, argOffset); // offset += serialzie EmitSerializeValue(il, type.GetTypeInfo(), item, index, tryEmitLoadCustomFormatter, argBytes, argOffset, argValue, argResolver); index++; } } // return startOffset- offset; argOffset.EmitLoad(); il.EmitLdloc(startOffsetLocal); il.Emit(OpCodes.Sub); il.Emit(OpCodes.Ret); } // offset += ***(ref bytes, offset.... static void EmitOffsetPlusEqual(ILGenerator il, Action loadEmit, Action emit, ArgumentField argBytes, ArgumentField argOffset) { argOffset.EmitLoad(); if (loadEmit != null) loadEmit(); argBytes.EmitLoad(); argOffset.EmitLoad(); emit(); il.Emit(OpCodes.Add); argOffset.EmitStore(); } 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; 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)) { EmitOffsetPlusEqual(il, null, () => { argValue.EmitLoad(); member.EmitLoadValue(il); if (t == typeof(byte[])) { il.EmitCall(MessagePackBinaryTypeInfo.WriteBytes); } else { il.EmitCall(MessagePackBinaryTypeInfo.TypeInfo.GetDeclaredMethods("Write" + t.Name).OrderByDescending(x => x.GetParameters().Length).First()); } }, argBytes, argOffset); } else { EmitOffsetPlusEqual(il, () => { argResolver.EmitLoad(); il.Emit(OpCodes.Call, getFormatterWithVerify.MakeGenericMethod(t)); }, () => { argValue.EmitLoad(); member.EmitLoadValue(il); argResolver.EmitLoad(); il.EmitCall(getSerialize(t)); }, argBytes, argOffset); } } // 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, ILGenerator il, Func tryEmitLoadCustomFormatter, int firstArgIndex) { var argBytes = new ArgumentField(il, firstArgIndex); var argOffset = new ArgumentField(il, firstArgIndex + 1); var argResolver = new ArgumentField(il, firstArgIndex + 2); var argReadSize = new ArgumentField(il, firstArgIndex + 3); // if(MessagePackBinary.IsNil) readSize = 1, return null; var falseLabel = il.DefineLabel(); argBytes.EmitLoad(); argOffset.EmitLoad(); il.EmitCall(MessagePackBinaryTypeInfo.IsNil); il.Emit(OpCodes.Brfalse_S, falseLabel); if (type.GetTypeInfo().IsClass) { argReadSize.EmitLoad(); il.EmitLdc_I4(1); il.Emit(OpCodes.Stind_I4); il.Emit(OpCodes.Ldnull); il.Emit(OpCodes.Ret); } else { il.Emit(OpCodes.Ldstr, "typecode is null, struct not supported"); il.Emit(OpCodes.Newobj, invalidOperationExceptionConstructor); il.Emit(OpCodes.Throw); } // var startOffset = offset; il.MarkLabel(falseLabel); var startOffsetLocal = il.DeclareLocal(typeof(int)); // [loc:0] argOffset.EmitLoad(); il.EmitStloc(startOffsetLocal); // var length = ReadMapHeader var length = il.DeclareLocal(typeof(int)); // [loc:1] argBytes.EmitLoad(); argOffset.EmitLoad(); argReadSize.EmitLoad(); if (info.IsIntKey) { il.EmitCall(MessagePackBinaryTypeInfo.ReadArrayHeader); } else { il.EmitCall(MessagePackBinaryTypeInfo.ReadMapHeader); } il.EmitStloc(length); EmitOffsetPlusReadSize(il, argOffset, argReadSize); // make local fields Label? gotoDefault = null; DeserializeInfo[] infoList; if (info.IsIntKey) { var maxKey = info.Members.Select(x => x.IntKey).DefaultIfEmpty(-1).Max(); var len = maxKey + 1; var intKeyMap = info.Members.ToDictionary(x => x.IntKey); infoList = Enumerable.Range(0, len) .Select(x => { ObjectSerializationInfo.EmittableMember member; if (intKeyMap.TryGetValue(x, out member)) { return new DeserializeInfo { MemberInfo = member, LocalField = il.DeclareLocal(member.Type), SwitchLabel = il.DefineLabel() }; } else { // return null MemberInfo, should filter null if (gotoDefault == null) { gotoDefault = il.DefineLabel(); } return new DeserializeInfo { MemberInfo = null, LocalField = null, SwitchLabel = gotoDefault.Value, }; } }) .ToArray(); } else { infoList = info.Members .Select(item => new DeserializeInfo { MemberInfo = item, LocalField = il.DeclareLocal(item.Type), // SwitchLabel = il.DefineLabel() }) .ToArray(); } // Read Loop(for var i = 0; i< length; i++) if (info.IsStringKey) { var automata = new AutomataDictionary(); for (int i = 0; i < info.Members.Length; i++) { automata.Add(info.Members[i].StringKey, i); } var buffer = il.DeclareLocal(typeof(byte).MakeByRefType(), true); var keyArraySegment = il.DeclareLocal(typeof(ArraySegment)); var longKey = il.DeclareLocal(typeof(ulong)); var p = il.DeclareLocal(typeof(byte*)); var rest = il.DeclareLocal(typeof(int)); // fixed (byte* buffer = &bytes[0]) { argBytes.EmitLoad(); il.EmitLdc_I4(0); il.Emit(OpCodes.Ldelema, typeof(byte)); il.EmitStloc(buffer); // for (int i = 0; i < len; i++) il.EmitIncrementFor(length, forILocal => { var readNext = il.DefineLabel(); var loopEnd = il.DefineLabel(); argBytes.EmitLoad(); argOffset.EmitLoad(); argReadSize.EmitLoad(); il.EmitCall(MessagePackBinaryTypeInfo.ReadStringSegment); il.EmitStloc(keyArraySegment); EmitOffsetPlusReadSize(il, argOffset, argReadSize); // p = buffer + arraySegment.Offset il.EmitLdloc(buffer); il.Emit(OpCodes.Conv_I); il.EmitLdloca(keyArraySegment); il.EmitCall(typeof(ArraySegment).GetRuntimeProperty("Offset").GetGetMethod()); il.Emit(OpCodes.Add); il.EmitStloc(p); // rest = arraySegment.Count il.EmitLdloca(keyArraySegment); il.EmitCall(typeof(ArraySegment).GetRuntimeProperty("Count").GetGetMethod()); il.EmitStloc(rest); // if(rest == 0) goto End il.EmitLdloc(rest); il.Emit(OpCodes.Brfalse, readNext); // gen automata name lookup automata.EmitMatch(il, p, rest, longKey, x => { var i = x.Value; if (infoList[i].MemberInfo != null) { EmitDeserializeValue(il, infoList[i], i, tryEmitLoadCustomFormatter, argBytes, argOffset, argResolver, argReadSize); il.Emit(OpCodes.Br, loopEnd); } else { il.Emit(OpCodes.Br, readNext); } }, () => { il.Emit(OpCodes.Br, readNext); }); il.MarkLabel(readNext); argReadSize.EmitLoad(); argBytes.EmitLoad(); argOffset.EmitLoad(); il.EmitCall(MessagePackBinaryTypeInfo.ReadNextBlock); il.Emit(OpCodes.Stind_I4); il.MarkLabel(loopEnd); EmitOffsetPlusReadSize(il, argOffset, argReadSize); }); // end fixed il.Emit(OpCodes.Ldc_I4_0); il.Emit(OpCodes.Conv_U); il.EmitStloc(buffer); } else { var key = il.DeclareLocal(typeof(int)); var switchDefault = il.DefineLabel(); il.EmitIncrementFor(length, forILocal => { var loopEnd = il.DefineLabel(); il.EmitLdloc(forILocal); il.EmitStloc(key); // switch... local = Deserialize il.EmitLdloc(key); il.Emit(OpCodes.Switch, infoList.Select(x => x.SwitchLabel).ToArray()); il.MarkLabel(switchDefault); // default, only read. readSize = MessagePackBinary.ReadNextBlock(bytes, offset); argReadSize.EmitLoad(); argBytes.EmitLoad(); argOffset.EmitLoad(); il.EmitCall(MessagePackBinaryTypeInfo.ReadNextBlock); il.Emit(OpCodes.Stind_I4); il.Emit(OpCodes.Br, loopEnd); if (gotoDefault != null) { il.MarkLabel(gotoDefault.Value); il.Emit(OpCodes.Br, switchDefault); } var i = 0; foreach (var item in infoList) { if (item.MemberInfo != null) { il.MarkLabel(item.SwitchLabel); EmitDeserializeValue(il, item, i++, tryEmitLoadCustomFormatter, argBytes, argOffset, argResolver, argReadSize); il.Emit(OpCodes.Br, loopEnd); } } // offset += readSize il.MarkLabel(loopEnd); EmitOffsetPlusReadSize(il, argOffset, argReadSize); }); } // finish readSize: readSize = offset - startOffset; argReadSize.EmitLoad(); argOffset.EmitLoad(); il.EmitLdloc(startOffsetLocal); il.Emit(OpCodes.Sub); il.Emit(OpCodes.Stind_I4); // create result object var structLocal = EmitNewObject(il, type, info, infoList); // IMessagePackSerializationCallbackReceiver.OnAfterDeserialize() if (type.GetTypeInfo().ImplementedInterfaces.Any(x => x == typeof(IMessagePackSerializationCallbackReceiver))) { // call directly var runtimeMethods = type.GetRuntimeMethods().Where(x => x.Name == "OnAfterDeserialize").ToArray(); if (runtimeMethods.Length == 1) { if (info.IsClass) { il.Emit(OpCodes.Dup); } else { il.EmitLdloca(structLocal); } il.Emit(OpCodes.Call, runtimeMethods[0]); // don't use EmitCall helper(must use 'Call') } else { if (info.IsStruct) { il.EmitLdloc(structLocal); il.Emit(OpCodes.Box, type); } else { il.Emit(OpCodes.Dup); } il.EmitCall(onAfterDeserialize); } } if (info.IsStruct) { il.Emit(OpCodes.Ldloc, structLocal); } il.Emit(OpCodes.Ret); } static void EmitOffsetPlusReadSize(ILGenerator il, ArgumentField argOffset, ArgumentField argReadSize) { argOffset.EmitLoad(); argReadSize.EmitLoad(); il.Emit(OpCodes.Ldind_I4); il.Emit(OpCodes.Add); argOffset.EmitStore(); } static void EmitDeserializeValue(ILGenerator il, DeserializeInfo info, int index, Func tryEmitLoadCustomFormatter, ArgumentField argBytes, ArgumentField argOffset, ArgumentField argResolver, ArgumentField argReadSize) { var member = info.MemberInfo; var t = member.Type; var emitter = tryEmitLoadCustomFormatter(index, member); if (emitter != null) { emitter(); argBytes.EmitLoad(); argOffset.EmitLoad(); argResolver.EmitLoad(); argReadSize.EmitLoad(); il.EmitCall(typeof(IMessagePackFormatter<>).MakeGenericType(t).GetRuntimeMethod("Deserialize", new[] { typeof(byte[]), typeof(int), typeof(IFormatterResolver), refInt })); } else if (IsOptimizeTargetType(t)) { il.EmitLdarg(1); il.EmitLdarg(2); il.EmitLdarg(4); if (t == typeof(byte[])) { il.EmitCall(MessagePackBinaryTypeInfo.ReadBytes); } else { il.EmitCall(MessagePackBinaryTypeInfo.TypeInfo.GetDeclaredMethods("Read" + t.Name).OrderByDescending(x => x.GetParameters().Length).First()); } } else { argResolver.EmitLoad(); il.EmitCall(getFormatterWithVerify.MakeGenericMethod(t)); argBytes.EmitLoad(); argOffset.EmitLoad(); argResolver.EmitLoad(); argReadSize.EmitLoad(); il.EmitCall(getDeserialize(t)); } il.EmitStloc(info.LocalField); } static LocalBuilder EmitNewObject(ILGenerator il, Type type, ObjectSerializationInfo info, DeserializeInfo[] members) { if (info.IsClass) { foreach (var item in info.ConstructorParameters) { var local = members.First(x => x.MemberInfo == item); il.EmitLdloc(local.LocalField); } il.Emit(OpCodes.Newobj, info.BestmatchConstructor); foreach (var item in members.Where(x => x.MemberInfo != null && x.MemberInfo.IsWritable)) { il.Emit(OpCodes.Dup); il.EmitLdloc(item.LocalField); item.MemberInfo.EmitStoreValue(il); } return null; } else { var result = il.DeclareLocal(type); if (info.BestmatchConstructor == null) { il.Emit(OpCodes.Ldloca, result); il.Emit(OpCodes.Initobj, type); } else { foreach (var item in info.ConstructorParameters) { var local = members.First(x => x.MemberInfo == item); il.EmitLdloc(local.LocalField); } il.Emit(OpCodes.Newobj, info.BestmatchConstructor); il.Emit(OpCodes.Stloc, result); } foreach (var item in members.Where(x => x.MemberInfo != null && x.MemberInfo.IsWritable)) { il.EmitLdloca(result); il.EmitLdloc(item.LocalField); item.MemberInfo.EmitStoreValue(il); } return result; // struct returns local result field } } static bool IsOptimizeTargetType(Type type) { if (type == typeof(Int16) || type == typeof(Int32) || type == typeof(Int64) || type == typeof(UInt16) || type == typeof(UInt32) || type == typeof(UInt64) || type == typeof(Single) || type == typeof(Double) || type == typeof(bool) || type == typeof(byte) || type == typeof(sbyte) || type == typeof(char) // not includes DateTime and String and Binary. //|| type == typeof(DateTime) //|| type == typeof(string) //|| type == typeof(byte[]) ) { return true; } return false; } // EmitInfos... static readonly Type refByte = typeof(byte[]).MakeByRefType(); static readonly Type refInt = typeof(int).MakeByRefType(); static readonly MethodInfo getFormatterWithVerify = typeof(FormatterResolverExtensions).GetRuntimeMethods().First(x => x.Name == "GetFormatterWithVerify"); static readonly Func getSerialize = t => typeof(IMessagePackFormatter<>).MakeGenericType(t).GetRuntimeMethod("Serialize", new[] { refByte, typeof(int), t, typeof(IFormatterResolver) }); static readonly Func getDeserialize = t => typeof(IMessagePackFormatter<>).MakeGenericType(t).GetRuntimeMethod("Deserialize", new[] { typeof(byte[]), typeof(int), typeof(IFormatterResolver), refInt }); // static readonly ConstructorInfo dictionaryConstructor = typeof(ByteArrayStringHashTable).GetTypeInfo().DeclaredConstructors.First(x => { var p = x.GetParameters(); return p.Length == 1 && p[0].ParameterType == typeof(int); }); // static readonly MethodInfo dictionaryAdd = typeof(ByteArrayStringHashTable).GetRuntimeMethod("Add", new[] { typeof(string), typeof(int) }); // static readonly MethodInfo dictionaryTryGetValue = typeof(ByteArrayStringHashTable).GetRuntimeMethod("TryGetValue", new[] { typeof(ArraySegment), refInt }); static readonly ConstructorInfo invalidOperationExceptionConstructor = typeof(System.InvalidOperationException).GetTypeInfo().DeclaredConstructors.First(x => { var p = x.GetParameters(); return p.Length == 1 && p[0].ParameterType == typeof(string); }); static readonly MethodInfo onBeforeSerialize = typeof(IMessagePackSerializationCallbackReceiver).GetRuntimeMethod("OnBeforeSerialize", Type.EmptyTypes); static readonly MethodInfo onAfterDeserialize = typeof(IMessagePackSerializationCallbackReceiver).GetRuntimeMethod("OnAfterDeserialize", Type.EmptyTypes); static readonly ConstructorInfo objectCtor = typeof(object).GetTypeInfo().DeclaredConstructors.First(x => x.GetParameters().Length == 0); internal static class MessagePackBinaryTypeInfo { 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, 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) }); public static MethodInfo WriteArrayHeader = typeof(MessagePackBinary).GetRuntimeMethod("WriteArrayHeader", new[] { refByte, typeof(int), typeof(int) }); public static MethodInfo WritePositiveFixedIntUnsafe = typeof(MessagePackBinary).GetRuntimeMethod("WritePositiveFixedIntUnsafe", new[] { refByte, typeof(int), typeof(int) }); public static MethodInfo WriteInt32 = typeof(MessagePackBinary).GetRuntimeMethod("WriteInt32", new[] { refByte, typeof(int), typeof(int) }); public static MethodInfo WriteBytes = typeof(MessagePackBinary).GetRuntimeMethod("WriteBytes", new[] { refByte, typeof(int), typeof(byte[]) }); public static MethodInfo WriteNil = typeof(MessagePackBinary).GetRuntimeMethod("WriteNil", new[] { refByte, typeof(int) }); public static MethodInfo ReadBytes = typeof(MessagePackBinary).GetRuntimeMethod("ReadBytes", new[] { typeof(byte[]), typeof(int), refInt }); public static MethodInfo ReadInt32 = typeof(MessagePackBinary).GetRuntimeMethod("ReadInt32", new[] { typeof(byte[]), typeof(int), refInt }); public static MethodInfo ReadString = typeof(MessagePackBinary).GetRuntimeMethod("ReadString", new[] { typeof(byte[]), typeof(int), refInt }); public static MethodInfo ReadStringSegment = typeof(MessagePackBinary).GetRuntimeMethod("ReadStringSegment", new[] { typeof(byte[]), typeof(int), refInt }); public static MethodInfo IsNil = typeof(MessagePackBinary).GetRuntimeMethod("IsNil", new[] { typeof(byte[]), typeof(int) }); public static MethodInfo ReadNextBlock = typeof(MessagePackBinary).GetRuntimeMethod("ReadNextBlock", new[] { typeof(byte[]), typeof(int) }); public static MethodInfo WriteStringUnsafe = typeof(MessagePackBinary).GetRuntimeMethod("WriteStringUnsafe", new[] { refByte, typeof(int), typeof(string), typeof(int) }); public static MethodInfo WriteStringBytes = typeof(MessagePackBinary).GetRuntimeMethod("WriteStringBytes", new[] { refByte, typeof(int), typeof(byte[]) }); public static MethodInfo WriteRaw = typeof(MessagePackBinary).GetRuntimeMethod("WriteRaw", new[] { refByte, typeof(int), typeof(byte[]) }); public static MethodInfo ReadArrayHeader = typeof(MessagePackBinary).GetRuntimeMethod("ReadArrayHeader", new[] { typeof(byte[]), typeof(int), refInt }); public static MethodInfo ReadMapHeader = typeof(MessagePackBinary).GetRuntimeMethod("ReadMapHeader", new[] { typeof(byte[]), typeof(int), refInt }); static MessagePackBinaryTypeInfo() { } } 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.GetTypeInfo().GetProperty(default(string), default(BindingFlags))); public static readonly MethodInfo TypeGetField = ExpressionUtility.GetMethodInfo((Type t) => t.GetTypeInfo().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 { public ObjectSerializationInfo.EmittableMember MemberInfo { get; set; } public LocalBuilder LocalField { get; set; } public Label SwitchLabel { get; set; } } } internal delegate int AnonymousSerializeFunc(byte[][] stringByteKeysField, object[] customFormatters, ref byte[] bytes, int offset, T value, IFormatterResolver resolver); internal delegate T AnonymousDeserializeFunc(object[] customFormatters, byte[] bytes, int offset, IFormatterResolver resolver, out int readSize); internal class AnonymousSerializableFormatter : IMessagePackFormatter { readonly byte[][] stringByteKeysField; readonly object[] serializeCustomFormatters; readonly object[] deserializeCustomFormatters; readonly AnonymousSerializeFunc serialize; readonly AnonymousDeserializeFunc deserialize; public AnonymousSerializableFormatter(byte[][] stringByteKeysField, object[] serializeCustomFormatters, object[] deserializeCustomFormatters, AnonymousSerializeFunc serialize, AnonymousDeserializeFunc deserialize) { this.stringByteKeysField = stringByteKeysField; this.serializeCustomFormatters = serializeCustomFormatters; this.deserializeCustomFormatters = deserializeCustomFormatters; this.serialize = serialize; this.deserialize = deserialize; } public int Serialize(ref byte[] bytes, int offset, T value, IFormatterResolver formatterResolver) { if (serialize == null) throw new InvalidOperationException(this.GetType().Name + " does not support Serialize."); return serialize(stringByteKeysField, serializeCustomFormatters, ref bytes, offset, value, formatterResolver); } public T Deserialize(byte[] bytes, int offset, IFormatterResolver formatterResolver, out int readSize) { if (deserialize == null) throw new InvalidOperationException(this.GetType().Name + " does not support Deserialize."); return deserialize(deserializeCustomFormatters, bytes, offset, formatterResolver, out readSize); } } internal class ObjectSerializationInfo { public Type Type { get; set; } public bool IsIntKey { get; set; } public bool IsStringKey { get { return !IsIntKey; } } public bool IsClass { get; set; } public bool IsStruct { get { return !IsClass; } } public ConstructorInfo BestmatchConstructor { get; set; } public EmittableMember[] ConstructorParameters { get; set; } public EmittableMember[] Members { get; set; } ObjectSerializationInfo() { } public static ObjectSerializationInfo CreateOrNull(Type type, bool forceStringKey, bool contractless, bool allowPrivate) { var ti = type.GetTypeInfo(); var isClass = ti.IsClass || ti.IsInterface || ti.IsAbstract; var contractAttr = ti.GetCustomAttribute(); var dataContractAttr = ti.GetCustomAttribute(); if (contractAttr == null && dataContractAttr == null && !forceStringKey && !contractless) { return null; } var isIntKey = true; var intMembers = new Dictionary(); var stringMembers = new Dictionary(); if (forceStringKey || contractless || (contractAttr != null && contractAttr.KeyAsPropertyName)) { // All public members are serialize target except [Ignore] member. isIntKey = !(forceStringKey || (contractAttr != null && contractAttr.KeyAsPropertyName)); var hiddenIntKey = 0; foreach (var item in type.GetRuntimeProperties()) { if (item.GetCustomAttribute(true) != null) continue; if (item.GetCustomAttribute(true) != null) continue; if (item.IsIndexer()) continue; var getMethod = item.GetGetMethod(true); var setMethod = item.GetSetMethod(true); var member = new EmittableMember { PropertyInfo = item, IsReadable = (getMethod != null) && (allowPrivate || getMethod.IsPublic) && !getMethod.IsStatic, IsWritable = (setMethod != null) && (allowPrivate || setMethod.IsPublic) && !setMethod.IsStatic, StringKey = item.Name }; if (!member.IsReadable && !member.IsWritable) continue; member.IntKey = hiddenIntKey++; if (isIntKey) { intMembers.Add(member.IntKey, member); } else { stringMembers.Add(member.StringKey, member); } } foreach (var item in type.GetRuntimeFields()) { if (item.GetCustomAttribute(true) != null) continue; if (item.GetCustomAttribute(true) != null) continue; if (item.GetCustomAttribute(true) != null) continue; if (item.IsStatic) continue; var member = new EmittableMember { FieldInfo = item, IsReadable = allowPrivate || item.IsPublic, IsWritable = allowPrivate || (item.IsPublic && !item.IsInitOnly), StringKey = item.Name }; if (!member.IsReadable && !member.IsWritable) continue; member.IntKey = hiddenIntKey++; if (isIntKey) { intMembers.Add(member.IntKey, member); } else { stringMembers.Add(member.StringKey, member); } } } else { // Public members with KeyAttribute except [Ignore] member. var searchFirst = true; var hiddenIntKey = 0; foreach (var item in type.GetRuntimeProperties()) { if (item.GetCustomAttribute(true) != null) continue; if (item.GetCustomAttribute(true) != null) continue; if (item.IsIndexer()) continue; var getMethod = item.GetGetMethod(true); var setMethod = item.GetSetMethod(true); var member = new EmittableMember { PropertyInfo = item, IsReadable = (getMethod != null) && (allowPrivate || getMethod.IsPublic) && !getMethod.IsStatic, IsWritable = (setMethod != null) && (allowPrivate || setMethod.IsPublic) && !setMethod.IsStatic, }; if (!member.IsReadable && !member.IsWritable) continue; KeyAttribute key; if (contractAttr != null) { // MessagePackObjectAttribute key = item.GetCustomAttribute(true); if (key == null) { throw new MessagePackDynamicObjectResolverException("all public members must mark KeyAttribute or IgnoreMemberAttribute." + " type: " + type.FullName + " member:" + item.Name); } if (key.IntKey == null && key.StringKey == null) throw new MessagePackDynamicObjectResolverException("both IntKey and StringKey are null." + " type: " + type.FullName + " member:" + item.Name); } else { // DataContractAttribute var pseudokey = item.GetCustomAttribute(true); if (pseudokey == null) { throw new MessagePackDynamicObjectResolverException("all public members must mark DataMemberAttribute or IgnoreMemberAttribute." + " type: " + type.FullName + " member:" + item.Name); } // use Order first if (pseudokey.Order != -1) { key = new KeyAttribute(pseudokey.Order); } else if (pseudokey.Name != null) { key = new KeyAttribute(pseudokey.Name); } else { key = new KeyAttribute(item.Name); // use property name } } if (searchFirst) { searchFirst = false; isIntKey = key.IntKey != null; } else { if ((isIntKey && key.IntKey == null) || (!isIntKey && key.StringKey == null)) { throw new MessagePackDynamicObjectResolverException("all members key type must be same." + " type: " + type.FullName + " member:" + item.Name); } } if (isIntKey) { member.IntKey = key.IntKey.Value; if (intMembers.ContainsKey(member.IntKey)) throw new MessagePackDynamicObjectResolverException("key is duplicated, all members key must be unique." + " type: " + type.FullName + " member:" + item.Name); intMembers.Add(member.IntKey, member); } else { member.StringKey = key.StringKey; if (stringMembers.ContainsKey(member.StringKey)) throw new MessagePackDynamicObjectResolverException("key is duplicated, all members key must be unique." + " type: " + type.FullName + " member:" + item.Name); member.IntKey = hiddenIntKey++; stringMembers.Add(member.StringKey, member); } } foreach (var item in type.GetRuntimeFields()) { if (item.GetCustomAttribute(true) != null) continue; if (item.GetCustomAttribute(true) != null) continue; if (item.GetCustomAttribute(true) != null) continue; if (item.IsStatic) continue; var member = new EmittableMember { FieldInfo = item, IsReadable = allowPrivate || item.IsPublic, IsWritable = allowPrivate || (item.IsPublic && !item.IsInitOnly), }; if (!member.IsReadable && !member.IsWritable) continue; KeyAttribute key; if (contractAttr != null) { // MessagePackObjectAttribute key = item.GetCustomAttribute(true); if (key == null) { throw new MessagePackDynamicObjectResolverException("all public members must mark KeyAttribute or IgnoreMemberAttribute." + " type: " + type.FullName + " member:" + item.Name); } if (key.IntKey == null && key.StringKey == null) throw new MessagePackDynamicObjectResolverException("both IntKey and StringKey are null." + " type: " + type.FullName + " member:" + item.Name); } else { // DataContractAttribute var pseudokey = item.GetCustomAttribute(true); if (pseudokey == null) { throw new MessagePackDynamicObjectResolverException("all public members must mark DataMemberAttribute or IgnoreMemberAttribute." + " type: " + type.FullName + " member:" + item.Name); } // use Order first if (pseudokey.Order != -1) { key = new KeyAttribute(pseudokey.Order); } else if (pseudokey.Name != null) { key = new KeyAttribute(pseudokey.Name); } else { key = new KeyAttribute(item.Name); // use property name } } if (searchFirst) { searchFirst = false; isIntKey = key.IntKey != null; } else { if ((isIntKey && key.IntKey == null) || (!isIntKey && key.StringKey == null)) { throw new MessagePackDynamicObjectResolverException("all members key type must be same." + " type: " + type.FullName + " member:" + item.Name); } } if (isIntKey) { member.IntKey = key.IntKey.Value; if (intMembers.ContainsKey(member.IntKey)) throw new MessagePackDynamicObjectResolverException("key is duplicated, all members key must be unique." + " type: " + type.FullName + " member:" + item.Name); intMembers.Add(member.IntKey, member); } else { member.StringKey = key.StringKey; if (stringMembers.ContainsKey(member.StringKey)) throw new MessagePackDynamicObjectResolverException("key is duplicated, all members key must be unique." + " type: " + type.FullName + " member:" + item.Name); member.IntKey = hiddenIntKey++; stringMembers.Add(member.StringKey, member); } } } // GetConstructor IEnumerator ctorEnumerator = null; var ctor = ti.DeclaredConstructors.Where(x => x.IsPublic).SingleOrDefault(x => x.GetCustomAttribute(false) != null); if (ctor == null) { ctorEnumerator = ti.DeclaredConstructors.Where(x => x.IsPublic).OrderBy(x => x.GetParameters().Length) .GetEnumerator(); if (ctorEnumerator.MoveNext()) { ctor = ctorEnumerator.Current; } } // struct allows null ctor if (ctor == null && isClass) throw new MessagePackDynamicObjectResolverException("can't find public constructor. type:" + type.FullName); var constructorParameters = new List(); if (ctor != null) { var constructorLookupDictionary = stringMembers.ToLookup(x => x.Key, x => x, StringComparer.OrdinalIgnoreCase); do { constructorParameters.Clear(); var ctorParamIndex = 0; foreach (var item in ctor.GetParameters()) { EmittableMember paramMember; if (isIntKey) { if (intMembers.TryGetValue(ctorParamIndex, out paramMember)) { if (item.ParameterType == paramMember.Type && paramMember.IsReadable) { constructorParameters.Add(paramMember); } else { if (ctorEnumerator != null) { ctor = null; continue; } else { throw new MessagePackDynamicObjectResolverException("can't find matched constructor parameter, parameterType mismatch. type:" + type.FullName + " parameterIndex:" + ctorParamIndex + " paramterType:" + item.ParameterType.Name); } } } else { if (ctorEnumerator != null) { ctor = null; continue; } else { throw new MessagePackDynamicObjectResolverException("can't find matched constructor parameter, index not found. type:" + type.FullName + " parameterIndex:" + ctorParamIndex); } } } else { var hasKey = constructorLookupDictionary[item.Name]; var len = hasKey.Count(); if (len != 0) { if (len != 1) { if (ctorEnumerator != null) { ctor = null; continue; } else { throw new MessagePackDynamicObjectResolverException("duplicate matched constructor parameter name:" + type.FullName + " parameterName:" + item.Name + " paramterType:" + item.ParameterType.Name); } } paramMember = hasKey.First().Value; if (item.ParameterType == paramMember.Type && paramMember.IsReadable) { constructorParameters.Add(paramMember); } else { if (ctorEnumerator != null) { ctor = null; continue; } else { throw new MessagePackDynamicObjectResolverException("can't find matched constructor parameter, parameterType mismatch. type:" + type.FullName + " parameterName:" + item.Name + " paramterType:" + item.ParameterType.Name); } } } else { if (ctorEnumerator != null) { ctor = null; continue; } else { throw new MessagePackDynamicObjectResolverException("can't find matched constructor parameter, index not found. type:" + type.FullName + " parameterName:" + item.Name); } } } ctorParamIndex++; } } while (TryGetNextConstructor(ctorEnumerator, ref ctor)); if (ctor == null) { throw new MessagePackDynamicObjectResolverException("can't find matched constructor. type:" + type.FullName); } } EmittableMember[] members; if (isIntKey) { members = intMembers.Values.OrderBy(x => x.IntKey).ToArray(); } else { members = stringMembers.Values .OrderBy(x => { var attr = x.GetDataMemberAttribute(); if (attr == null) return int.MaxValue; return attr.Order; }) .ToArray(); } return new ObjectSerializationInfo { Type = type, IsClass = isClass, BestmatchConstructor = ctor, ConstructorParameters = constructorParameters.ToArray(), IsIntKey = isIntKey, Members = members, }; } static bool TryGetNextConstructor(IEnumerator ctorEnumerator, ref ConstructorInfo ctor) { if (ctorEnumerator == null || ctor != null) { return false; } if (ctorEnumerator.MoveNext()) { ctor = ctorEnumerator.Current; return true; } else { ctor = null; return false; } } public class EmittableMember { public bool IsProperty { get { return PropertyInfo != null; } } public bool IsField { get { return FieldInfo != null; } } public bool IsWritable { get; set; } public bool IsReadable { get; set; } public int IntKey { get; set; } public string StringKey { get; set; } 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 { var mi = IsProperty ? (MemberInfo)PropertyInfo : FieldInfo; return mi.DeclaringType.GetTypeInfo().IsValueType; } } public MessagePackFormatterAttribute GetMessagePackFormatterAttribtue() { if (IsProperty) { return (MessagePackFormatterAttribute)PropertyInfo.GetCustomAttribute(true); } else { return (MessagePackFormatterAttribute)FieldInfo.GetCustomAttribute(true); } } public DataMemberAttribute GetDataMemberAttribute() { if (IsProperty) { return (DataMemberAttribute)PropertyInfo.GetCustomAttribute(true); } else { return (DataMemberAttribute)FieldInfo.GetCustomAttribute(true); } } public void EmitLoadValue(ILGenerator il) { if (IsProperty) { il.EmitCall(PropertyInfo.GetGetMethod(true)); } else { il.Emit(OpCodes.Ldfld, FieldInfo); } } public void EmitStoreValue(ILGenerator il) { if (IsProperty) { il.EmitCall(PropertyInfo.GetSetMethod(true)); } else { il.Emit(OpCodes.Stfld, FieldInfo); } } //public object ReflectionLoadValue(object value) //{ // if (IsProperty) // { // return PropertyInfo.GetValue(value); // } // else // { // return FieldInfo.GetValue(value); // } //} //public void ReflectionStoreValue(object obj, object value) //{ // if (IsProperty) // { // PropertyInfo.SetValue(obj, value); // } // else // { // FieldInfo.SetValue(obj, value); // } //} } } internal class MessagePackDynamicObjectResolverException : Exception { public MessagePackDynamicObjectResolverException(string message) : base(message) { } } } #endif #endif