protected override void ReadNotEmpty(ReaderMethodBuilderContext context) { var il = context.Il; var readers = GetReaders(context); var readersField = context.Context.InitConstField(Type, 0, readers.Select(pair => pair.Value).ToArray()); context.Context.InitConstField(Type, 1, readers.Select(pair => pair.Key).ToArray()); context.LoadData(); // stack: [data] context.LoadIndexByRef(); // stack: [data, ref index] context.LoadResultByRef(); // stack: [data, ref index, ref result] context.LoadContext(); // stack: [data, ref index, ref result, context] context.LoadField(readersField); // stack: [data, ref index, ref result, context, readers] il.Ldloc(context.TypeCode); // stack: [data, ref index, ref result, context, readers, typeCode] il.Ldelem(typeof(IntPtr)); // stack: [data, ref index, ref result, context, readers[typeCode]] il.Dup(); // stack: [data, ref index, ref result, context, readers[typeCode], readers[typeCode]] var skipValueLabel = il.DefineLabel("skipValue"); il.Brfalse(skipValueLabel); // if(readers[typeCode] == 0) goto skipValue; var parameterTypes = new[] { typeof(byte *), typeof(int).MakeByRefType(), typeof(object).MakeByRefType(), typeof(ReaderContext) }; il.Calli(CallingConventions.Standard, typeof(void), parameterTypes); // readers[typeCode](data, ref index, ref result, context); stack: [] il.Ret(); il.MarkLabel(skipValueLabel); il.Pop(); il.Pop(); il.Pop(); il.Pop(); il.Pop(); context.IncreaseIndexBy1(); context.SkipValue(); }
protected override void ReadNotEmpty(ReaderMethodBuilderContext context) { var il = context.Il; var source = il.DeclareLocal(typeof(IntPtr)); context.GoToCurrentLocation(); // stack: [&data[index]] il.Stloc(source); // source = &data[index]; stack: [] context.IncreaseIndexBy1(); // skip type code context.SkipValue(); context.GoToCurrentLocation(); // stack: [&data[index]] il.Ldloc(source); // stack: [&data[index], source] il.Sub(); // stack: [&data[index] - source = data length] var length = il.DeclareLocal(typeof(int)); il.Stloc(length); // length = &data[index] - source; stack: [] var array = il.DeclareLocal(typeof(byte[])); il.Ldloc(length); // stack: [length] il.Newarr(typeof(byte)); // stack: [new byte[length]] il.Stloc(array); // array = new byte[length]; stack: [] var dest = il.DeclareLocal(typeof(byte).MakeByRefType(), true); il.Ldloc(array); // stack: [array] il.Ldc_I4(0); // stack: [array, 0] il.Ldelema(typeof(byte)); // stack: [&array[0]] il.Stloc(dest); // dest = &array[0]; stack: [] il.Ldloc(dest); // stack: [dest] il.Ldloc(source); // stack: [dest, source] il.Ldloc(length); // stack: [dest, source, length] il.Cpblk(); // dest = source; stack: [] il.FreePinnedLocal(dest); // dest = null; stack: [] var argumentType = Type.GetGenericArguments()[0]; context.LoadResultByRef(); // stack: [ref result] context.LoadSerializerId(); // stack: [ref result, serializerId] il.Ldloc(array); // stack: [ref result, serializerId, array] context.LoadReader(argumentType); // stack: [ref result, serializerId, array, reader<arg>] context.LoadSerializerId(); // stack: [ref result, serializerId, array, reader<arg>, serializerId] il.Newobj(readerInvoker.GetConstructor(new[] { typeof(IntPtr), typeof(long) })); // stack: [ref result, serializerId, array, new ReaderInvoker(reader<arg>, serializerId)] il.Ldftn(readerInvoker.GetMethod("Read", BindingFlags.Instance | BindingFlags.Public)); var readDataFuncType = typeof(Func <,>).MakeGenericType(typeof(byte[]), argumentType); il.Newobj(readDataFuncType.GetConstructor(new[] { typeof(object), typeof(IntPtr) })); // stack: [ref result, serializerId, array, new Func<byte[], arg>(..)] var rawDataType = typeof(RawData <>).MakeGenericType(argumentType); il.Newobj(rawDataType.GetConstructor(new[] { typeof(long), typeof(byte[]), readDataFuncType })); // stack: [ref result, new RawData(serializerId, array, func)] il.Ldftn(rawDataType.GetMethod("GetValue", BindingFlags.Instance | BindingFlags.Public)); // stack: [ref result, new RawData(..), RawData.GetValue] var factoryType = typeof(Func <>).MakeGenericType(argumentType); il.Newobj(factoryType.GetConstructor(new[] { typeof(object), typeof(IntPtr) })); // stack: [ref result, new Func<arg>(new RawData(), RawData.GetValue)] il.Newobj(Type.GetConstructor(new[] { factoryType })); // stack: [ref result, new Lazy<arg>(new Func<arg>(new RawData(), RawData.GetValue))] il.Stind(Type); // result = new Lazy<argument>(array, func); stack: [] }
protected override void ReadNotEmpty(ReaderMethodBuilderContext context) { context.IncreaseIndexBy1(); var readersField = context.Context.InitConstField(Type, 0, primitiveReaders.Select(pair => pair.Value).ToArray()); context.Context.InitConstField(Type, 1, primitiveReaders.Select(pair => pair.Key).ToArray()); var il = context.Il; context.GoToCurrentLocation(); // stack: [&data[index]] context.LoadResultByRef(); // stack: [&data[index], ref result] context.SkipValue(); context.LoadField(readersField); // stack: [&data[index], ref result, readers] il.Ldloc(context.TypeCode); // stack: [&data[index], ref result, readers, typeCode] il.Ldelem(typeof(IntPtr)); // stack: [&data[index], ref result, readers[typeCode]] il.Calli(CallingConventions.Standard, typeof(void), new[] { typeof(byte *), Type.MakeByRefType() }); // readers[typeCode](&data[index], ref result); stack: [] }
protected override void ReadNotEmpty(ReaderMethodBuilderContext context) { MemberInfo[] dataMembers; ulong[] hashCodes; BuildMembersTable(context.Context, out hashCodes, out dataMembers); var il = context.Il; var end = context.Length; var typeCode = context.TypeCode; var setters = dataMembers.Select(member => member == null ? default(KeyValuePair <Delegate, IntPtr>) : GetMemberSetter(context.Context, member)).ToArray(); var settersField = context.Context.InitConstField(Type, 0, setters.Select(pair => pair.Value).ToArray()); context.Context.InitConstField(Type, 1, setters.Select(pair => pair.Key).ToArray()); var hashCodesField = context.Context.InitConstField(Type, 2, hashCodes); context.IncreaseIndexBy1(); // index = index + 1 context.AssertTypeCode(GroBufTypeCode.Object); il.Ldc_I4(4); context.AssertLength(); context.GoToCurrentLocation(); // stack: [&data[index]] il.Ldind(typeof(uint)); // stack: [(uint)data[index] = data length] context.IncreaseIndexBy4(); // index = index + 4; stack: [data length] il.Dup(); // stack: [data length, data length] il.Stloc(end); // end = data length; stack: [data length] if (!Type.IsValueType) { context.LoadResultByRef(); // stack: [data length, ref result] il.Ldind(Type); // stack: [data length, result] var notNullLabel = il.DefineLabel("notNull"); il.Brtrue(notNullLabel); // if(result != null) goto notNull; stack: [data length] context.LoadResultByRef(); // stack: [data length, ref result] ObjectConstructionHelper.EmitConstructionOfType(Type, il); il.Stind(Type); // result = new type(); stack: [data length] il.MarkLabel(notNullLabel); } context.StoreObject(Type); var doneLabel = il.DefineLabel("done"); il.Brfalse(doneLabel); // if(data length == 0) goto done; stack: [] il.Ldloc(end); // stack: [data length] context.AssertLength(); // stack: [] il.Ldloc(end); // stack: [data length] context.LoadIndex(); // stack: [data length, index] il.Add(); // stack: [data length + index] il.Stloc(end); // end = data length + index var cycleStartLabel = il.DefineLabel("cycleStart"); il.MarkLabel(cycleStartLabel); il.Ldc_I4(9); context.AssertLength(); context.GoToCurrentLocation(); // stack: [&data[index]] il.Ldind(typeof(long)); // stack: [*(int64*)&data[index] = hashCode] context.IncreaseIndexBy8(); // index = index + 8; stack: [*(int64*)&data[index] = hashCode] il.Dup(); // stack: [hashCode, hashCode] il.Ldc_I8(dataMembers.Length); // stack: [hashCode, hashCode, (int64)hashCodes.Length] il.Rem(true); // stack: [hashCode, hashCode % hashCodes.Length] il.Conv <int>(); // stack: [hashCode, (int)(hashCode % hashCodes.Length)] var idx = il.DeclareLocal(typeof(int)); il.Stloc(idx); // idx = (int)(hashCode % hashCodes.Length); stack: [hashCode] context.LoadField(hashCodesField); // stack: [hashCode, hashCodes] il.Ldloc(idx); // stack: [hashCode, hashCodes, idx] il.Ldelem(typeof(long)); // stack: [hashCode, hashCodes[idx]] var skipDataLabel = il.DefineLabel("skipData"); il.Bne_Un(skipDataLabel); // if(hashCode != hashCodes[idx]) goto skipData; stack: [] // Read data context.LoadData(); // stack: [pinnedData] context.LoadIndexByRef(); // stack: [pinnedData, ref index] context.LoadResultByRef(); // stack: [pinnedData, ref index, ref result] context.LoadContext(); // stack: [pinnedData, ref index, ref result, context] context.LoadField(settersField); // stack: [pinnedData, ref index, ref result, context, setters] il.Ldloc(idx); // stack: [pinnedData, ref index, ref result, context, setters, idx] il.Ldelem(typeof(IntPtr)); // stack: [pinnedData, ref index, ref result, context, setters[idx]] var parameterTypes = new[] { typeof(byte *), typeof(int).MakeByRefType(), Type.MakeByRefType(), typeof(ReaderContext) }; il.Calli(CallingConventions.Standard, typeof(void), parameterTypes); // setters[idx](pinnedData, ref index, ref result, context); stack: [] var checkIndexLabel = il.DefineLabel("checkIndex"); il.Br(checkIndexLabel); // goto checkIndex il.MarkLabel(skipDataLabel); // Skip data context.GoToCurrentLocation(); // stack: [&data[index]] il.Ldind(typeof(byte)); // stack: [data[index]] il.Stloc(typeCode); // typeCode = data[index]; stack: [] context.IncreaseIndexBy1(); // index = index + 1 context.CheckTypeCode(); context.SkipValue(); il.MarkLabel(checkIndexLabel); context.LoadIndex(); // stack: [index] il.Ldloc(end); // stack: [index, end] il.Blt(cycleStartLabel, true); // if(index < end) goto cycleStart; stack: [] var onDeserializedMethod = Type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) .SingleOrDefault(method => method.GetCustomAttribute <OnDeserializedAttribute>() != null); if (onDeserializedMethod != null) { var parameters = onDeserializedMethod.GetParameters(); if (parameters.Length != 1 || parameters[0].ParameterType != typeof(StreamingContext)) { throw new InvalidOperationException(string.Format("The method '{0}' marked with 'OnDeserialized' attribute must accept exactly one parameter of type '{1}'", onDeserializedMethod, typeof(StreamingContext).FullName)); } context.LoadResult(Type); il.Ldc_I4((int)StreamingContextStates.Other); il.Newobj(typeof(StreamingContext).GetConstructor(new[] { typeof(StreamingContextStates) })); il.Call(onDeserializedMethod); } il.MarkLabel(doneLabel); }
public void BuildReader(ReaderTypeBuilderContext readerTypeBuilderContext) { var method = new DynamicMethod("Read_" + Type.Name + "_" + Guid.NewGuid(), typeof(void), new[] { typeof(IntPtr), typeof(int).MakeByRefType(), Type.MakeByRefType(), typeof(ReaderContext) }, readerTypeBuilderContext.Module, true); readerTypeBuilderContext.SetReaderMethod(Type, method); using (var il = new GroboIL(method)) { var context = new ReaderMethodBuilderContext(readerTypeBuilderContext, il, !Type.IsValueType && IsReference); ReadTypeCodeAndCheck(context); // Read TypeCode and check if (!Type.IsValueType && IsReference) { // Read reference context.LoadContext(); // stack: [context] il.Ldfld(ReaderContext.ObjectsField); // stack: [context.objects] var notReadLabel = il.DefineLabel("notRead"); il.Brfalse(notReadLabel); context.LoadIndex(); // stack: [external index] context.LoadContext(); // stack: [external index, context] il.Ldfld(ReaderContext.StartField); // stack: [external index, context.start] il.Sub(); // stack: [external index - context.start] il.Stloc(context.Index); // index = external index - context.start; stack: [] context.LoadContext(); // stack: [context] il.Ldfld(ReaderContext.ObjectsField); // stack: [context.objects] il.Ldloc(context.Index); // stack: [context.objects, index] var obj = il.DeclareLocal(typeof(object)); il.Ldloca(obj); object dummy; il.Call(HackHelpers.GetMethodDefinition <Dictionary <int, object> >(dict => dict.TryGetValue(0, out dummy))); // stack: [context.objects.TryGetValue(index, out obj)] il.Brfalse(notReadLabel); // if(!context.objects.TryGetValue(index, out obj)) goto notRead; context.LoadResultByRef(); // stack: [ref result] il.Ldloc(obj); // stack: [ref result, obj] il.Castclass(Type); // stack: [ref result, (Type)obj] il.Stind(Type); // result = (Type)obj; stack: [] context.IncreaseIndexBy1(); // Skip type code context.SkipValue(); // Skip value - it has already been read il.Ret(); il.MarkLabel(notReadLabel); il.Ldloc(context.TypeCode); // stack: [typeCode] il.Ldc_I4((int)GroBufTypeCode.Reference); // stack: [typeCode, GroBufTypeCode.Reference] var readUsualLabel = il.DefineLabel("readUsual"); il.Bne_Un(readUsualLabel); // if(typeCode != GroBufTypeCode.Reference) goto readUsual; stack: [] context.LoadContext(); // stack: [context] il.Ldfld(ReaderContext.ObjectsField); // stack: [context.objects] var objectsIsNotNullLabel = il.DefineLabel("objectsIsNotNull"); il.Brtrue(objectsIsNotNullLabel); // if(context.objects != null) goto objectsIsNotNull; stack: [context.objects] il.Ldstr("Reference is not valid at this point"); il.Newobj(typeof(DataCorruptedException).GetConstructor(new[] { typeof(string) })); il.Throw(); il.MarkLabel(objectsIsNotNullLabel); context.IncreaseIndexBy1(); // index = index + 1; stack: [] il.Ldc_I4(4); context.AssertLength(); context.GoToCurrentLocation(); var reference = il.DeclareLocal(typeof(int)); il.Ldind(typeof(int)); // stack: [*(int*)data[index]] il.Stloc(reference); // reference = *(int*)data[index]; stack: [] context.IncreaseIndexBy4(); // index = index + 4; stack: [] il.Ldloc(context.Index); // stack: [index] il.Ldloc(reference); // stack: [index, reference] var goodReferenceLabel = il.DefineLabel("goodReference"); il.Bgt(goodReferenceLabel, false); // if(index > reference) goto goodReference; stack: [] il.Ldstr("Bad reference"); il.Newobj(typeof(DataCorruptedException).GetConstructor(new[] { typeof(string) })); il.Throw(); il.MarkLabel(goodReferenceLabel); context.LoadContext(); // stack: [context] il.Ldfld(ReaderContext.ObjectsField); // stack: [context.objects] il.Ldloc(reference); // stack: [context.objects, reference] il.Ldloca(obj); // stack: [context.objects, reference, ref obj] il.Call(HackHelpers.GetMethodDefinition <Dictionary <int, object> >(dict => dict.TryGetValue(0, out dummy))); // stack: [context.objects.TryGetValue(reference, out obj)] var readObjectLabel = il.DefineLabel("readObject"); il.Brfalse(readObjectLabel); // if(!context.objects.TryGetValue(reference, out obj)) goto readObjects; stack: [] context.LoadResultByRef(); // stack: [ref result] il.Ldloc(obj); // stack: [ref result, obj] il.Castclass(Type); // stack: [ref result, (Type)obj] il.Stind(Type); // result = (Type)obj; stack: [] il.Ret(); il.MarkLabel(readObjectLabel); // Referenced object has not been read - this means that the object reference belongs to is a property that had been deleted context.LoadData(); // stack: [data] il.Ldloc(reference); // stack: [data, reference] context.LoadContext(); // stack: [data, reference, context] il.Ldfld(ReaderContext.StartField); // stack: [data, reference, context.start] il.Add(); // stack: [data, reference + context.start] il.Stloc(reference); // reference += context.start; stack: [data] il.Ldloca(reference); // stack: [data, ref reference] context.LoadResultByRef(); // stack: [data, ref reference, ref result] context.LoadContext(); // stack: [data, ref reference, ref result, context] context.CallReader(Type); il.Ret(); il.MarkLabel(readUsualLabel); } ReadNotEmpty(context); // Read obj il.Ret(); } var @delegate = method.CreateDelegate(typeof(ReaderDelegate <>).MakeGenericType(Type)); var pointer = GroBufHelpers.ExtractDynamicMethodPointer(method); readerTypeBuilderContext.SetReaderPointer(Type, pointer, @delegate); }
protected override void ReadNotEmpty(ReaderMethodBuilderContext context) { var constructor = Type.GetConstructor(new[] { typeof(long), typeof(DateTimeKind) }); if (constructor == null) { throw new MissingConstructorException(Type, typeof(long), typeof(DateTimeKind)); } var il = context.Il; il.Ldloc(context.TypeCode); // stack: [typeCode] il.Ldc_I4((int)GroBufTypeCode.DateTimeNew); // stack: [typeCode, GroBufTypeCode.DateTimeNew] var processOldFormatLabel = il.DefineLabel("processOldFormat"); il.Bne_Un(processOldFormatLabel); // if(typeCode != GroBufTypeCode.DateTimeNew) goto processOldFormat; stack: [] context.IncreaseIndexBy1(); il.Ldc_I4(8); // stack: [8] context.AssertLength(); context.LoadResultByRef(); // stack: [ref result] context.GoToCurrentLocation(); // stack: [ref result, &data[index]] il.Ldind(typeof(long)); // stack: [ref result, (long)data[index]] il.Call(dateTimeFromBinaryMethod); // stack: [ref result, DateTime.FromBinary((long)data[index])] il.Stobj(Type); // result = DateTime.FromBinary((long)data[index]) context.IncreaseIndexBy8(); // index = index + 8 il.Ret(); il.MarkLabel(processOldFormatLabel); var okLabel = il.DefineLabel("ok"); il.Ldloc(context.TypeCode); // stack: [typeCode] il.Ldc_I4((int)GroBufTypeCode.DateTimeOld); // stack: [typeCode, GroBufTypeCode.DateTimeOld] il.Beq(okLabel); // if(typeCode == GroBufTypeCode.DateTimeOld) goto label il.Ldloc(context.TypeCode); // stack: [typeCode] il.Ldc_I4((int)GroBufTypeCode.Int64); // stack: [typeCode, GroBufTypeCode.Int64] il.Beq(okLabel); // if(typeCode == GroBufTypeCode.Int64) goto label context.SkipValue(); il.Ret(); il.MarkLabel(okLabel); context.IncreaseIndexBy1(); il.Ldc_I4(8); // stack: [8] context.AssertLength(); context.LoadResultByRef(); // stack: [ref result] context.GoToCurrentLocation(); // stack: [ref result, &data[index]] il.Ldind(typeof(long)); // stack: [ref result, (long)&data[index] = ticks] il.Dup(); // stack: [ref result, ticks, ticks] il.Ldc_I8(long.MinValue); // stack: [ref result, ticks, ticks, 0x8000000000000000] il.And(); // stack: [ref result, ticks, ticks & 0x8000000000000000] var notUtcLabel = il.DefineLabel("notUtc"); il.Brtrue(notUtcLabel); // if(ticks & 0x8000000000000000 != 0) goto notUtc; stack: [ref result, ticks] il.Ldc_I4((int)DateTimeKind.Utc); // stack: [ref result, ticks, DateTimeKind.Utc] il.Newobj(constructor); // stack: [ref result, new DateTimeOld(ticks, DateTimeKind.Utc)] il.Stobj(Type); context.IncreaseIndexBy8(); il.Ret(); il.MarkLabel(notUtcLabel); context.IncreaseIndexBy8(); il.Ldc_I4(1); context.AssertLength(); il.Ldc_I8(long.MaxValue); // stack: [ref result, ticks, 0x7FFFFFFFFFFFFFFF] il.And(); // stack: [ref result, ticks & 0x7FFFFFFFFFFFFFFF] context.GoToCurrentLocation(); // stack: [ref result, ticks & 0x7FFFFFFFFFFFFFFF, &data[index]] il.Ldind(typeof(byte)); // stack: [ref result, ticks & 0x7FFFFFFFFFFFFFFF, (DateTimeKind)data[index] = kind] il.Newobj(constructor); // stack: [ref result, new DateTimeOld(ticks, kind)] il.Stobj(Type); context.IncreaseIndexBy1(); }