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 void BuildConstructor(Type type, ObjectSerializationInfo info, ConstructorInfo method, FieldBuilder dictionaryField, ILGenerator il) { il.EmitLdarg(0); il.Emit(OpCodes.Call, objectCtor); il.EmitLdarg(0); il.EmitLdc_I4(info.Members.Length); il.Emit(OpCodes.Newobj, dictionaryConstructor); foreach (var item in info.Members) { il.Emit(OpCodes.Dup); il.Emit(OpCodes.Ldstr, item.StringKey); il.EmitLdc_I4(item.IntKey); il.EmitCall(dictionaryAdd); } il.Emit(OpCodes.Stfld, dictionaryField); il.Emit(OpCodes.Ret); }
// 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, FieldBuilder dictionaryField, ILGenerator il) { // if(MessagePackBinary.IsNil) readSize = 1, return null; var falseLabel = il.DefineLabel(); il.EmitLdarg(1); il.EmitLdarg(2); il.EmitCall(MessagePackBinaryTypeInfo.IsNil); il.Emit(OpCodes.Brfalse_S, falseLabel); if (type.GetTypeInfo().IsClass) { il.EmitLdarg(4); 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] il.EmitLdarg(2); il.EmitStloc(startOffsetLocal); // var length = ReadMapHeader var length = il.DeclareLocal(typeof(int)); // [loc:1] il.EmitLdarg(1); il.EmitLdarg(2); il.EmitLdarg(4); if (info.IsIntKey) { il.EmitCall(MessagePackBinaryTypeInfo.ReadArrayHeader); } else { il.EmitCall(MessagePackBinaryTypeInfo.ReadMapHeader); } il.EmitStloc(length); EmitOffsetPlusReadSize(il); // make local fields Label?gotoDefault = null; DeserializeInfo[] infoList; if (info.IsIntKey) { var maxKey = info.Members.Select(x => x.IntKey).DefaultIfEmpty(0).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++) { var key = il.DeclareLocal(typeof(int)); var switchDefault = il.DefineLabel(); var loopEnd = il.DefineLabel(); var stringKeyTrue = il.DefineLabel(); il.EmitIncrementFor(length, forILocal => { if (info.IsStringKey) { // get string key -> dictionary lookup il.EmitLdarg(0); il.Emit(OpCodes.Ldfld, dictionaryField); il.EmitLdarg(1); il.EmitLdarg(2); il.EmitLdarg(4); il.EmitCall(MessagePackBinaryTypeInfo.ReadString); il.EmitLdloca(key); il.EmitCall(dictionaryTryGetValue); EmitOffsetPlusReadSize(il); il.Emit(OpCodes.Brtrue_S, stringKeyTrue); il.EmitLdarg(4); il.EmitLdarg(1); il.EmitLdarg(2); il.EmitCall(MessagePackBinaryTypeInfo.ReadNextBlock); il.Emit(OpCodes.Stind_I4); il.Emit(OpCodes.Br, loopEnd); il.MarkLabel(stringKeyTrue); } else { 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); il.EmitLdarg(4); il.EmitLdarg(1); il.EmitLdarg(2); 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); } foreach (var item in infoList) { if (item.MemberInfo != null) { il.MarkLabel(item.SwitchLabel); EmitDeserializeValue(il, item); il.Emit(OpCodes.Br, loopEnd); } } // offset += readSize il.MarkLabel(loopEnd); EmitOffsetPlusReadSize(il); }); } // finish readSize: readSize = offset - startOffset; il.EmitLdarg(4); il.EmitLdarg(2); 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); }
// 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, MethodBuilder method, ILGenerator il) { // if(value == null) return WriteNil if (type.GetTypeInfo().IsClass) { var elseBody = il.DefineLabel(); il.EmitLdarg(3); il.Emit(OpCodes.Brtrue_S, elseBody); il.EmitLdarg(1); il.EmitLdarg(2); 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) { if (info.IsStruct) { il.EmitLdarga(3); } else { il.EmitLdarg(3); } il.Emit(OpCodes.Call, runtimeMethods[0]); // don't use EmitCall helper(must use 'Call') } else { il.EmitLdarg(3); if (info.IsStruct) { il.Emit(OpCodes.Box, type); } il.EmitCall(onBeforeSerialize); } } // var startOffset = offset; var startOffsetLocal = il.DeclareLocal(typeof(int)); // [loc:0] il.EmitLdarg(2); il.EmitStloc(startOffsetLocal); if (info.IsIntKey) { // use Array var maxKey = info.Members.Where(x => x.IsReadable).Select(x => x.IntKey).DefaultIfEmpty(0).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); } }); for (int i = 0; i <= maxKey; i++) { ObjectSerializationInfo.EmittableMember member; if (intKeyMap.TryGetValue(i, out member)) { // offset += serialzie EmitSerializeValue(il, type.GetTypeInfo(), member); } else { // Write Nil as Blanc EmitOffsetPlusEqual(il, null, () => { il.EmitCall(MessagePackBinaryTypeInfo.WriteNil); }); } } } 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); } }); foreach (var item in info.Members.Where(x => x.IsReadable)) { // offset += writekey if (info.IsStringKey) { EmitOffsetPlusEqual(il, null, () => { // embed string and bytesize il.Emit(OpCodes.Ldstr, item.StringKey); il.EmitLdc_I4(StringEncoding.UTF8.GetByteCount(item.StringKey)); il.EmitCall(MessagePackBinaryTypeInfo.WriteStringUnsafe); }); } // offset += serialzie EmitSerializeValue(il, type.GetTypeInfo(), item); } } // return startOffset- offset; il.EmitLdarg(2); il.EmitLdloc(startOffsetLocal); il.Emit(OpCodes.Sub); il.Emit(OpCodes.Ret); }