private static LocalBuilder EmitNewObject(ILGenerator il, Type type, UnionSerializationInfo info, DeserializeInfo[] members) { if (info.IsClass) { foreach (UnionSerializationInfo.EmittableMember item in info.MethodParameters) { DeserializeInfo local = members.First(x => x.MemberInfo == item); il.EmitLdloc(local.LocalField); } il.Emit(OpCodes.Call, info.NewMethod); return(null); } else { LocalBuilder result = il.DeclareLocal(type); foreach (var item in info.MethodParameters) { var local = members.First(x => x.MemberInfo == item); il.EmitLdloc(local.LocalField); } il.Emit(OpCodes.Call, info.NewMethod); il.Emit(OpCodes.Stloc, result); return(result); // struct returns local result field } }
static void EmitDeserializeUnionCase( ILGenerator il, Type type, UnionSerializationInfo info, LocalBuilder unionKey, FieldBuilder stringByteKeysField, ArgumentField reader, ArgumentField argOptions, LocalBuilder localResolver ) { // options.Security.DepthStep(ref reader); argOptions.EmitLoad(); il.EmitCall(getSecurityFromOptions); reader.EmitLdarg(); il.EmitCall(securityDepthStep); // var length = ReadMapHeader(ref byteSequence); LocalBuilder length = il.DeclareLocal(typeof(int)); // [loc:1] reader.EmitLdarg(); if (info.IsIntKey) { il.EmitCall(MessagePackReaderTypeInfo.ReadArrayHeader); } else { il.EmitCall(MessagePackReaderTypeInfo.ReadMapHeader); } il.EmitStloc(length); // 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 => { UnionSerializationInfo.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); } LocalBuilder buffer = il.DeclareLocal(typeof(ReadOnlySpan <byte>)); LocalBuilder longKey = il.DeclareLocal(typeof(ulong)); // for (int i = 0; i < len; i++) il.EmitIncrementFor(length, forILocal => { Label readNext = il.DefineLabel(); Label loopEnd = il.DefineLabel(); reader.EmitLdarg(); il.EmitCall(ReadStringSpan); il.EmitStloc(buffer); // gen automata name lookup automata.EmitMatch( il, buffer, longKey, x => { var i = x.Value; if (infoList[i].MemberInfo != null) { EmitDeserializeValue(il, infoList[i], i, reader, argOptions, localResolver); il.Emit(OpCodes.Br, loopEnd); } else { il.Emit(OpCodes.Br, readNext); } }, () => { il.Emit(OpCodes.Br, readNext); }); il.MarkLabel(readNext); reader.EmitLdarg(); il.EmitCall(MessagePackReaderTypeInfo.Skip); il.MarkLabel(loopEnd); }); } else { LocalBuilder key = il.DeclareLocal(typeof(int)); Label switchDefault = il.DefineLabel(); il.EmitIncrementFor(length, forILocal => { Label 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. reader.ReadNextBlock(); reader.EmitLdarg(); il.EmitCall(MessagePackReaderTypeInfo.Skip); il.Emit(OpCodes.Br, loopEnd); if (gotoDefault != null) { il.MarkLabel(gotoDefault.Value); il.Emit(OpCodes.Br, switchDefault); } var i = 0; foreach (DeserializeInfo item in infoList) { if (item.MemberInfo != null) { il.MarkLabel(item.SwitchLabel); EmitDeserializeValue(il, item, i++, reader, argOptions, localResolver); il.Emit(OpCodes.Br, loopEnd); } } il.MarkLabel(loopEnd); }); } // create result union case LocalBuilder 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); } } // reader.Depth--; reader.EmitLdarg(); il.Emit(OpCodes.Dup); il.EmitCall(readerDepthGet); il.Emit(OpCodes.Ldc_I4_1); il.Emit(OpCodes.Sub_Ovf); il.EmitCall(readerDepthSet); if (info.IsStruct) { il.Emit(OpCodes.Ldloc, structLocal); } }
static void EmitSerializeUnionCase( ILGenerator il, Type type, TypeInfo ti, UnionSerializationInfo info, FieldBuilder stringByteKeysField, ArgumentField argWriter, ArgumentField argValue, ArgumentField argOptions, LocalBuilder localResolver ) { if (info.IsIntKey) { // use Array var maxKey = info.Members.Select(x => x.IntKey).DefaultIfEmpty(-1).Max(); var intKeyMap = info.Members.ToDictionary(x => x.IntKey); var len = maxKey + 1; argWriter.EmitLoad(); il.EmitLdc_I4(len); il.EmitCall(MessagePackWriterTypeInfo.WriteArrayHeader); var index = 0; for (int i = 0; i <= maxKey; i++) { UnionSerializationInfo.EmittableMember member; if (intKeyMap.TryGetValue(i, out member)) { EmitSerializeValue(il, ti, member, index++, argWriter, argValue, argOptions, localResolver); } else { // Write Nil as Blanc argWriter.EmitLoad(); il.EmitCall(MessagePackWriterTypeInfo.WriteNil); } } } else { // use Map var writeCount = info.Members.Count(); argWriter.EmitLoad(); il.EmitLdc_I4(writeCount); ////if (writeCount <= MessagePackRange.MaxFixMapCount) ////{ //// il.EmitCall(MessagePackWriterTypeInfo.WriteFixedMapHeaderUnsafe); ////} ////else { il.EmitCall(MessagePackWriterTypeInfo.WriteMapHeader); } var index = 0; foreach (UnionSerializationInfo.EmittableMember item in info.Members) { argWriter.EmitLoad(); il.EmitLoadThis(); il.EmitLdfld(stringByteKeysField); il.EmitLdc_I4(index); il.Emit(OpCodes.Ldelem_Ref); il.Emit(OpCodes.Call, ReadOnlySpanFromByteArray); // convert byte[] to ReadOnlySpan<byte> // Optimize, WriteRaw(Unity, large) or UnsafeMemory32/64.WriteRawX var valueLen = CodeGenHelpers.GetEncodedStringBytes(item.StringKey).Length; if (valueLen <= MessagePackRange.MaxFixStringLength) { if (UnsafeMemory.Is32Bit) { il.EmitCall(typeof(UnsafeMemory32).GetRuntimeMethod("WriteRaw" + valueLen, new[] { typeof(MessagePackWriter).MakeByRefType(), typeof(ReadOnlySpan <byte>) })); } else { il.EmitCall(typeof(UnsafeMemory64).GetRuntimeMethod("WriteRaw" + valueLen, new[] { typeof(MessagePackWriter).MakeByRefType(), typeof(ReadOnlySpan <byte>) })); } } else { il.EmitCall(MessagePackWriterTypeInfo.WriteRaw); } EmitSerializeValue(il, type.GetTypeInfo(), item, index, argWriter, argValue, argOptions, localResolver); index++; } } }
// T Deserialize([arg:1]ref MessagePackReader reader, [arg:2]MessagePackSerializerOptions options); static void BuildDeserialize( Type type, UnionCaseInfo[] infos, MethodBuilder method, FieldBuilder[] stringByteKeysFields, ILGenerator il, int firstArgIndex ) { var ti = type.GetTypeInfo(); var reader = new ArgumentField(il, firstArgIndex, @ref: true); var argOptions = new ArgumentField(il, firstArgIndex + 1); // if(MessagePackBinary.TryReadNil()) { return null; } Label falseLabel = il.DefineLabel(); reader.EmitLdarg(); il.EmitCall(MessagePackReaderTypeInfo.TryReadNil); il.Emit(OpCodes.Brfalse_S, falseLabel); if (ti.IsClass) { 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); } il.MarkLabel(falseLabel); // IFormatterResolver resolver = options.Resolver; LocalBuilder localResolver = il.DeclareLocal(typeof(IFormatterResolver)); argOptions.EmitLdarg(); il.EmitCall(getResolverFromOptions); il.EmitStloc(localResolver); // read-array header and validate, reader.ReadArrayHeader() != 2) throw; Label rightLabel = il.DefineLabel(); reader.EmitLdarg(); il.EmitCall(MessagePackReaderTypeInfo.ReadArrayHeader); il.EmitLdc_I4(2); il.Emit(OpCodes.Beq_S, rightLabel); il.Emit(OpCodes.Ldstr, "Invalid Union data was detected. Type:" + type.FullName); il.Emit(OpCodes.Newobj, invalidOperationExceptionConstructor); il.Emit(OpCodes.Throw); il.MarkLabel(rightLabel); // read key LocalBuilder key = il.DeclareLocal(typeof(int)); reader.EmitLdarg(); il.EmitCall(MessagePackReaderTypeInfo.ReadInt32); il.EmitStloc(key); // switch->read LocalBuilder result = il.DeclareLocal(type); Label loopEnd = il.DefineLabel(); if (ti.IsClass) { il.Emit(OpCodes.Ldnull); il.EmitStloc(result); } il.Emit(OpCodes.Ldloc, key); var switchLabels = infos.Select(x => new { Label = il.DefineLabel(), Info = x }).ToArray(); il.Emit(OpCodes.Switch, switchLabels.Select(x => x.Label).ToArray()); // default reader.EmitLdarg(); il.EmitCall(MessagePackReaderTypeInfo.Skip); il.Emit(OpCodes.Br, loopEnd); foreach (var item in switchLabels) { il.MarkLabel(item.Label); EmitDeserializeUnionCase( il, type, UnionSerializationInfo.CreateOrNull(type, item.Info), key, stringByteKeysFields[item.Info.Tag], reader, argOptions, localResolver ); il.Emit(OpCodes.Stloc, result); il.Emit(OpCodes.Br, loopEnd); } il.MarkLabel(loopEnd); il.Emit(OpCodes.Ldloc, result); il.Emit(OpCodes.Ret); }
// void Serialize(ref [arg:1]MessagePackWriter writer, [arg:2]T value, [arg:3]MessagePackSerializerOptions options); static void BuildSerialize( Type type, UnionCaseInfo[] infos, MethodBuilder method, FieldBuilder[] stringByteKeysFields, ILGenerator il, int firstArgIndex ) { var tag = getTag(type); var ti = type.GetTypeInfo(); var argWriter = new ArgumentField(il, firstArgIndex); var argValue = new ArgumentField(il, firstArgIndex + 1, type); var argOptions = new ArgumentField(il, firstArgIndex + 2); // if(value == null) return WriteNil if (ti.IsClass) { Label elseBody = il.DefineLabel(); argValue.EmitLoad(); il.Emit(OpCodes.Brtrue_S, elseBody); argWriter.EmitLoad(); il.EmitCall(MessagePackWriterTypeInfo.WriteNil); il.Emit(OpCodes.Ret); il.MarkLabel(elseBody); } // IMessagePackSerializationCallbackReceiver.OnBeforeSerialize() if (ti.ImplementedInterfaces.Any(x => x == typeof(IMessagePackSerializationCallbackReceiver))) { // call directly MethodInfo[] 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); } } // IFormatterResolver resolver = options.Resolver; LocalBuilder localResolver = il.DeclareLocal(typeof(IFormatterResolver)); argOptions.EmitLoad(); il.EmitCall(getResolverFromOptions); il.EmitStloc(localResolver); // writer.WriteArrayHeader(2, false); argWriter.EmitLdarg(); il.EmitLdc_I4(2); il.EmitCall(MessagePackWriterTypeInfo.WriteArrayHeader); // writer.Write(value.Tag) argWriter.EmitLdarg(); if (ti.IsClass) { argValue.EmitLdarg(); } else { argValue.EmitLdarga(); } il.EmitCall(tag); il.EmitCall(MessagePackWriterTypeInfo.WriteInt32); var loopEnd = il.DefineLabel(); // switch-case (offset += resolver.GetFormatter.Serialize(with cast) var switchLabels = infos.Select(x => new { Label = il.DefineLabel(), Info = x }).ToArray(); if (ti.IsClass) { argValue.EmitLdarg(); } else { argValue.EmitLdarga(); } il.EmitCall(tag); il.Emit(OpCodes.Switch, switchLabels.Select(x => x.Label).ToArray()); il.Emit(OpCodes.Br, loopEnd); // default foreach (var item in switchLabels) { il.MarkLabel(item.Label); EmitSerializeUnionCase( il, type, ti, UnionSerializationInfo.CreateOrNull(type, item.Info), stringByteKeysFields[item.Info.Tag], argWriter, argValue, argOptions, localResolver ); il.Emit(OpCodes.Br, loopEnd); } // return; il.MarkLabel(loopEnd); il.Emit(OpCodes.Ret); }
static void EmitDeserializeUnionCase(ILGenerator il, Type type, UnionSerializationInfo info, LocalBuilder unionKey, FieldBuilder stringByteKeysField) { // var startOffset = offset; var startOffsetLocal = il.DeclareLocal(typeof(int)); // [loc:0] il.EmitLdarg(2); il.EmitStloc(startOffsetLocal); // var length = ReadMapHeader var length = il.DeclareLocal(typeof(int)); 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(-1).Max(); var len = maxKey + 1; var intKeyMap = info.Members.ToDictionary(x => x.IntKey); infoList = Enumerable.Range(0, len) .Select(x => { UnionSerializationInfo.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 <byte>)); var longKey = il.DeclareLocal(typeof(ulong)); var p = il.DeclareLocal(typeof(byte *)); var rest = il.DeclareLocal(typeof(int)); // fixed (byte* buffer = &bytes[0]) { il.EmitLdarg(1); 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(); il.EmitLdarg(1); il.EmitLdarg(2); il.EmitLdarg(4); il.EmitCall(MessagePackBinaryTypeInfo.ReadStringSegment); il.EmitStloc(keyArraySegment); EmitOffsetPlusReadSize(il); // p = buffer + arraySegment.Offset il.EmitLdloc(buffer); il.Emit(OpCodes.Conv_I); il.EmitLdloca(keyArraySegment); il.EmitCall(typeof(ArraySegment <byte>).GetRuntimeProperty("Offset").GetGetMethod()); il.Emit(OpCodes.Add); il.EmitStloc(p); // rest = arraySegment.Count il.EmitLdloca(keyArraySegment); il.EmitCall(typeof(ArraySegment <byte>).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]); il.Emit(OpCodes.Br, loopEnd); } else { il.Emit(OpCodes.Br, readNext); } }, () => { il.Emit(OpCodes.Br, readNext); }); il.MarkLabel(readNext); il.EmitLdarg(4); il.EmitLdarg(1); il.EmitLdarg(2); il.EmitCall(MessagePackBinaryTypeInfo.ReadNextBlock); il.Emit(OpCodes.Stind_I4); il.MarkLabel(loopEnd); EmitOffsetPlusReadSize(il); }); // 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); 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 union case 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); } }
// T Deserialize([arg:1]byte[] bytes, [arg:2]int offset, [arg:3]IFormatterResolver formatterResolver, [arg:4]out int readSize); static void BuildDeserialize(Type type, Microsoft.FSharp.Reflection.UnionCaseInfo[] infos, MethodBuilder method, FieldBuilder[] stringByteKeysFields, ILGenerator il) { var ti = type.GetTypeInfo(); // 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 (ti.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); } // read-array header and validate, ReadArrayHeader(bytes, offset, out readSize) != 2) throw; il.MarkLabel(falseLabel); var startOffset = il.DeclareLocal(typeof(int)); il.EmitLdarg(2); il.EmitStloc(startOffset); var rightLabel = il.DefineLabel(); il.EmitLdarg(1); il.EmitLdarg(2); il.EmitLdarg(4); il.EmitCall(MessagePackBinaryTypeInfo.ReadArrayHeader); il.EmitLdc_I4(2); il.Emit(OpCodes.Beq_S, rightLabel); il.Emit(OpCodes.Ldstr, "Invalid Union data was detected. Type:" + type.FullName); il.Emit(OpCodes.Newobj, invalidOperationExceptionConstructor); il.Emit(OpCodes.Throw); il.MarkLabel(rightLabel); EmitOffsetPlusReadSize(il); // read key var key = il.DeclareLocal(typeof(int)); il.EmitLdarg(1); il.EmitLdarg(2); il.EmitLdarg(4); il.EmitCall(MessagePackBinaryTypeInfo.ReadInt32); il.EmitStloc(key); EmitOffsetPlusReadSize(il); // switch->read var result = il.DeclareLocal(type); var loopEnd = il.DefineLabel(); if (ti.IsClass) { il.Emit(OpCodes.Ldnull); il.EmitStloc(result); } il.Emit(OpCodes.Ldloc, key); var switchLabels = infos.Select(x => new { Label = il.DefineLabel(), Info = x }).ToArray(); il.Emit(OpCodes.Switch, switchLabels.Select(x => x.Label).ToArray()); // default il.EmitLdarg(2); il.EmitLdarg(1); il.EmitLdarg(2); il.EmitCall(MessagePackBinaryTypeInfo.ReadNextBlock); il.Emit(OpCodes.Add); il.EmitStarg(2); il.Emit(OpCodes.Br, loopEnd); foreach (var item in switchLabels) { il.MarkLabel(item.Label); EmitDeserializeUnionCase(il, type, UnionSerializationInfo.CreateOrNull(type, item.Info), key, stringByteKeysFields[item.Info.Tag]); il.Emit(OpCodes.Stloc, result); il.Emit(OpCodes.Br, loopEnd); } il.MarkLabel(loopEnd); // finish readSize = offset - startOffset; il.EmitLdarg(4); il.EmitLdarg(2); il.EmitLdloc(startOffset); il.Emit(OpCodes.Sub); il.Emit(OpCodes.Stind_I4); il.Emit(OpCodes.Ldloc, result); il.Emit(OpCodes.Ret); }
static void EmitSerializeUnionCase(ILGenerator il, Type type, TypeInfo ti, UnionSerializationInfo info, FieldBuilder stringByteKeysField) { // IMessagePackSerializationCallbackReceiver.OnBeforeSerialize() if (ti.ImplementedInterfaces.Any(x => x == typeof(IMessagePackSerializationCallbackReceiver))) { // call directly var runtimeMethods = type.GetRuntimeMethods().Where(x => x.Name == "OnBeforeSerialize").ToArray(); if (runtimeMethods.Length == 1) { il.EmitLoadArg(ti, 3); il.Emit(OpCodes.Call, runtimeMethods[0]); // don't use EmitCall helper(must use 'Call') } else { il.EmitLoadArg(ti, 3); if (info.IsStruct) { il.Emit(OpCodes.Box, type); } il.EmitCall(onBeforeSerialize); } } if (info.IsIntKey) { // use Array var maxKey = info.Members.Select(x => x.IntKey).DefaultIfEmpty(0).Max(); var intKeyMap = info.Members.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++) { UnionSerializationInfo.EmittableMember member; if (intKeyMap.TryGetValue(i, out member)) { // offset += serialzie EmitSerializeValue(il, ti, member); } else { // Write Nil as Blanc EmitOffsetPlusEqual(il, null, () => { il.EmitCall(MessagePackBinaryTypeInfo.WriteNil); }); } } } else { // use Map var writeCount = info.Members.Count(); EmitOffsetPlusEqual(il, null, () => { il.EmitLdc_I4(writeCount); if (writeCount <= MessagePackRange.MaxFixMapCount) { il.EmitCall(MessagePackBinaryTypeInfo.WriteFixedMapHeaderUnsafe); } else { il.EmitCall(MessagePackBinaryTypeInfo.WriteMapHeader); } }); var index = 0; foreach (var item in info.Members) { // offset += writekey EmitOffsetPlusEqual(il, null, () => { il.EmitLdarg(0); il.EmitLdfld(stringByteKeysField); 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); } index++; }); // offset += serialzie EmitSerializeValue(il, ti, item); } } }
// int Serialize([arg:1]ref byte[] bytes, [arg:2]int offset, [arg:3]T value, [arg:4]IFormatterResolver formatterResolver); static void BuildSerialize(Type type, Microsoft.FSharp.Reflection.UnionCaseInfo[] infos, MethodBuilder method, FieldBuilder[] stringByteKeysFields, ILGenerator il) { var tag = getTag(type); var ti = type.GetTypeInfo(); // if(value == null) return WriteNil var elseBody = il.DefineLabel(); var notFoundType = il.DefineLabel(); il.EmitLoadArg(ti, 3); il.Emit(OpCodes.Brtrue_S, elseBody); il.Emit(OpCodes.Br, notFoundType); il.MarkLabel(elseBody); // var startOffset = offset; var startOffsetLocal = il.DeclareLocal(typeof(int)); il.EmitLdarg(2); il.EmitStloc(startOffsetLocal); // offset += WriteFixedArrayHeaderUnsafe(,,2); EmitOffsetPlusEqual(il, null, () => { il.EmitLdc_I4(2); il.EmitCall(MessagePackBinaryTypeInfo.WriteFixedArrayHeaderUnsafe); }); // offset += WriteInt32(,,value.Tag) EmitOffsetPlusEqual(il, null, () => { il.EmitLoadArg(ti, 3); il.EmitCall(tag); il.EmitCall(MessagePackBinaryTypeInfo.WriteInt32); }); var loopEnd = il.DefineLabel(); // switch-case (offset += resolver.GetFormatter.Serialize(with cast) var switchLabels = infos.Select(x => new { Label = il.DefineLabel(), Info = x }).ToArray(); il.EmitLoadArg(ti, 3); il.EmitCall(tag); il.Emit(OpCodes.Switch, switchLabels.Select(x => x.Label).ToArray()); il.Emit(OpCodes.Br, loopEnd); // default foreach (var item in switchLabels) { il.MarkLabel(item.Label); EmitSerializeUnionCase(il, type, ti, UnionSerializationInfo.CreateOrNull(type, item.Info), stringByteKeysFields[item.Info.Tag]); il.Emit(OpCodes.Br, loopEnd); } // return startOffset- offset; il.MarkLabel(loopEnd); il.EmitLdarg(2); il.EmitLdloc(startOffsetLocal); il.Emit(OpCodes.Sub); il.Emit(OpCodes.Ret); // else, return WriteNil il.MarkLabel(notFoundType); il.EmitLdarg(1); il.EmitLdarg(2); il.EmitCall(MessagePackBinaryTypeInfo.WriteNil); il.Emit(OpCodes.Ret); }