protected override void InnerGenerate(ReaderGenerationContext context)
        {
            foreach (var field in type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))
            {
                context.Generator.Emit(OpCodes.Ldarg_1);
                context.Generator.Emit(OpCodes.Castclass, type);
                context.Generator.Emit(OpCodes.Ldarg_0);
                context.Generator.Emit(OpCodes.Castclass, type);
                context.Generator.Emit(OpCodes.Ldfld, field);

                if (!field.FieldType.IsValueType)
                {
                    var valuesNotEqualLabel = context.Generator.DefineLabel();

                    context.Generator.Emit(OpCodes.Dup);
                    context.Generator.Emit(OpCodes.Ldarg_0);
                    context.Generator.Emit(OpCodes.Castclass, type);
                    context.Generator.Emit(OpCodes.Bne_Un_S, valuesNotEqualLabel);

                    context.Generator.Emit(OpCodes.Pop);
                    context.Generator.Emit(OpCodes.Ldarg_1);
                    context.Generator.Emit(OpCodes.Castclass, type);

                    context.Generator.MarkLabel(valuesNotEqualLabel);
                }

                context.Generator.Emit(OpCodes.Stfld, field);
            }
        }
        protected override void InnerGenerate(ReaderGenerationContext context)
        {
            var factoryId = objectsForSurrogates.FindMatchingIndex(type);

            GenerateCallPostDeserializationHooks(context, factoryId);
            GenerateDesurrogate(context, factoryId);
        }
        private static void GenerateUpdateFields(ReaderGenerationContext context, Type formalType, LocalBuilder objectIdLocal)
        {
            var fields = context.DisableStamping
                ? ((TypeSimpleDescriptor)formalType).FieldsToDeserialize
                : ((TypeFullDescriptor)formalType).FieldsToDeserialize;

            foreach (var fieldOrType in fields)
            {
                if (fieldOrType.Field == null)
                {
                    GenerateReadField(context, fieldOrType.TypeToOmit, false);
                    context.Generator.Emit(OpCodes.Pop);
                    continue;
                }
                var field = fieldOrType.Field;

                if (field.IsDefined(typeof(TransientAttribute), false))
                {
                    if (field.IsDefined(typeof(ConstructorAttribute), false))
                    {
                        context.Generator.PushFieldInfoOntoStack(field);
                        context.PushDeserializedObjectOntoStack(objectIdLocal);

                        context.Generator.GenerateCodeCall <FieldInfo, object>(GenerateUpdateFieldsStaticHelper);
                    }
                    continue;
                }

                context.PushDeserializedObjectOntoStack(objectIdLocal);
                GenerateReadField(context, field.FieldType, false);
                context.Generator.Emit(OpCodes.Stfld, field);
            }
        }
        private static void SaveNewDeserializedObject(ReaderGenerationContext context, LocalBuilder objectIdLocal, Action generateNewObject)
        {
            context.PushDeserializedObjectsCollectionOntoStack();
            context.Generator.PushLocalValueOntoStack(objectIdLocal);

            generateNewObject();

            context.Generator.Emit(OpCodes.Call, Helpers.GetMethodInfo <AutoResizingList <object> >(x => x.SetItem(0, null)));
        }
        private static void GenerateReadMethod(ReaderGenerationContext context)
        {
            // method returns read methodInfo (or null)

            context.PushObjectReaderOntoStack();
            context.Generator.Emit(OpCodes.Call, Helpers.GetPropertyGetterInfo <ObjectReader, IdentifiedElementsList <MethodDescriptor> >(or => or.Methods));
            context.Generator.Emit(OpCodes.Call, Helpers.GetMethodInfo <IdentifiedElementsList <MethodDescriptor>, MethodDescriptor>(or => or.Read()));
            context.Generator.Emit(OpCodes.Call, Helpers.GetPropertyGetterInfo <MethodDescriptor, MethodInfo>(md => md.UnderlyingMethod));
        }
        internal static void GenerateReadPrimitive(ReaderGenerationContext context, Type type)
        {
            context.PushPrimitiveReaderOntoStack();
            var mname      = string.Concat("Read", type.Name);
            var readMethod = typeof(PrimitiveReader).GetMethod(mname);

            if (readMethod == null)
            {
                throw new ArgumentException("Method <<" + mname + ">> not found");
            }

            context.Generator.Emit(OpCodes.Call, readMethod);
        }
        protected override MethodInfo GenerateInner()
        {
            var dynamicMethod = new DynamicMethod(name, returnType, parameterTypes, typeof(Serializer), true);
            var generator     = dynamicMethod.GetILGenerator();
            var context       = new ReaderGenerationContext(generator, disableStamping, treatCollectionAsUserObject, objectIdArgument, objectReaderArgument);

            InnerGenerate(context);
            context.Generator.Emit(OpCodes.Ret);
#if DEBUG_FORMAT
            GeneratorHelper.DumpToLibrary <T>(context, x => InnerGenerate((ReaderGenerationContext)x), name);
#endif
            return(dynamicMethod);
        }
        private static void GenerateUpdateStructFields(ReaderGenerationContext context, Type formalType, LocalBuilder structLocal)
        {
            var fields = context.DisableStamping
                                ? ((TypeSimpleDescriptor)formalType).FieldsToDeserialize
                                : ((TypeFullDescriptor)formalType).FieldsToDeserialize;

            foreach (var field in fields)
            {
                if (field.Field == null)
                {
                    GenerateReadField(context, field.TypeToOmit, false);
                    context.Generator.Emit(OpCodes.Pop);
                    continue;
                }

                if (field.Field.IsDefined(typeof(TransientAttribute), false))
                {
                    if (field.Field.IsDefined(typeof(ConstructorAttribute), false))
                    {
                        context.Generator.PushLocalAddressOntoStack(structLocal);
                        context.Generator.PushFieldInfoOntoStack(field.Field);

                        context.Generator.GenerateCodeFCall <FieldInfo, object>(GenerateUpdateStructFieldsStaticHelper);

                        if (field.Field.FieldType.IsValueType)
                        {
                            context.Generator.Emit(OpCodes.Unbox_Any, field.Field.FieldType);
                        }

                        context.Generator.Emit(OpCodes.Stfld, field.Field);
                    }
                    continue;
                }

                context.Generator.PushLocalAddressOntoStack(structLocal);
                var type = field.TypeToOmit ?? field.Field.FieldType;

                GenerateReadField(context, type, false);
                if (field.Field != null)
                {
                    context.Generator.Emit(OpCodes.Stfld, field.Field);
                }
                else
                {
                    context.Generator.Emit(OpCodes.Pop); // struct local
                    context.Generator.Emit(OpCodes.Pop); // read value
                }
            }
        }
        protected override void InnerGenerate(ReaderGenerationContext context)
        {
            context.Generator.Emit(OpCodes.Ldarg_0);
            context.Generator.Emit(OpCodes.Ldarg_1);
            var addMethod = type.GetMethod("Add");

            if (addMethod.GetParameters().Length == 2)
            {
                context.Generator.Emit(OpCodes.Ldarg_2);
            }
            context.Generator.Emit(OpCodes.Callvirt, addMethod);

            if (addMethod.ReturnType != typeof(void))
            {
                context.Generator.Emit(OpCodes.Pop);
            }
        }
        private void GenerateDesurrogate(ReaderGenerationContext context, int factoryId)
        {
            if (factoryId == -1)
            {
                return;
            }

            var desurrogatedObjectLocal = context.Generator.DeclareLocal(typeof(object));

            // obtain surrogate factory
            context.PushObjectReaderOntoStack();
            context.Generator.PushFieldValueOntoStack <ObjectReader, SwapList>(x => x.objectsForSurrogates);
            context.Generator.PushIntegerOntoStack(factoryId);
            context.Generator.Call <SwapList>(x => x.GetByIndex(0));

            // recreate an object from the surrogate
            var delegateType = typeof(Func <,>).MakeGenericType(type, typeof(object));

            context.Generator.Emit(OpCodes.Castclass, delegateType);
            context.PushDeserializedObjectOntoStack(context.PushObjectIdOntoStack);
            context.Generator.Emit(OpCodes.Call, delegateType.GetMethod("Invoke"));

            context.Generator.StoreLocalValueFromStack(desurrogatedObjectLocal);

            // clone context
            context.PushObjectReaderOntoStack();
            context.Generator.PushFieldValueOntoStack <ObjectReader, Serializer.ReadMethods>(x => x.readMethods);
            context.Generator.PushFieldValueOntoStack <Serializer.ReadMethods, DynamicMethodProvider <CloneMethodDelegate> >(x => x.cloneContentMehtodsProvider);
            context.Generator.PushLocalValueOntoStack(desurrogatedObjectLocal);
            context.Generator.Call <object>(x => x.GetType());
            context.Generator.Call <DynamicMethodProvider <CloneMethodDelegate> >(x => x.GetOrCreate(typeof(void)));
            context.Generator.PushLocalValueOntoStack(desurrogatedObjectLocal);
            context.PushDeserializedObjectOntoStack(context.PushObjectIdOntoStack, true);
            context.Generator.Call <CloneMethodDelegate>(x => x.Invoke(null, null));

            //remove object reference from surrogatesWhileReading collection
            context.PushObjectReaderOntoStack();
            context.Generator.PushFieldValueOntoStack <ObjectReader, OneToOneMap <int, object> >(x => x.surrogatesWhileReading);

            context.PushObjectIdOntoStack();
            context.Generator.Call <OneToOneMap <int, object> >(x => x.Remove(0));
        }
        private static void GenerateUpdateElements(ReaderGenerationContext context, Type formalType, LocalBuilder objectIdLocal)
        {
            if (typeof(ISpeciallySerializable).IsAssignableFrom(formalType))
            {
                context.PushDeserializedObjectOntoStack(objectIdLocal);
                context.Generator.Emit(OpCodes.Castclass, typeof(ISpeciallySerializable));
                context.PushPrimitiveReaderOntoStack();
                context.Generator.GenerateCodeCall <ISpeciallySerializable, PrimitiveReader>(ObjectReader.LoadAndVerifySpeciallySerializableAndVerify);
                return;
            }

            CollectionMetaToken collectionToken;

            if (!CollectionMetaToken.TryGetCollectionMetaToken(formalType, out collectionToken))
            {
                throw new InvalidOperationException(InternalErrorMessage);
            }

            GenerateFillCollection(context, collectionToken, formalType, objectIdLocal);
        }
        private static void GenerateTouchObject(ReaderGenerationContext context, Type formalType)
        {
            var finish = context.Generator.DefineLabel();

            context.PushObjectIdOntoStack();
            context.PushDeserializedObjectsCollectionOntoStack();
            context.Generator.PushPropertyValueOntoStack <AutoResizingList <object>, int>(x => x.Count);
            // if (refId < deserializedObjects.Count) return;
            context.Generator.Emit(OpCodes.Blt, finish);

            if (CreateObjectGenerator.TryFillBody(context.Generator, formalType, context.TreatCollectionAsUserObject, x =>
            {
                context.PushObjectReaderOntoStack();
                context.PushObjectIdOntoStack();
            }))
            {
                context.Generator.Call <ObjectReader>(x => x.SetObjectByReferenceId(0, null));
            }

            context.Generator.MarkLabel(finish);
        }
 internal static void GenerateReadNotPrecreated(ReaderGenerationContext context, Type formalType, Action pushObjectIdOntoStackAction)
 {
     if (formalType.IsValueType)
     {
         context.PushDeserializedObjectsCollectionOntoStack();
         pushObjectIdOntoStackAction();
         GenerateReadField(context, formalType, true);
         context.Generator.Emit(OpCodes.Call, Helpers.GetMethodInfo <AutoResizingList <object> >(x => x.SetItem(0, null)));
     }
     else if (formalType.IsArray)
     {
         GenerateReadArray(context, formalType, pushObjectIdOntoStackAction);
     }
     else if (typeof(MulticastDelegate).IsAssignableFrom(formalType))
     {
         GenerateReadDelegate(context, formalType, pushObjectIdOntoStackAction);
     }
     else
     {
         throw new InvalidOperationException(InternalErrorMessage + "GenerateReadNotPrecreated");
     }
 }
        private static void GenerateReadDelegate(ReaderGenerationContext context, Type type, Action pushObjectIdOntoStackAction)
        {
            var invocationListLengthLocal = context.Generator.DeclareLocal(typeof(int));
            var targetLocal = context.Generator.DeclareLocal(typeof(object));

            GenerateReadPrimitive(context, typeof(int));
            context.Generator.StoreLocalValueFromStack(invocationListLengthLocal);

            GeneratorHelper.GenerateLoop(context, invocationListLengthLocal, cl =>
            {
                GenerateReadField(context, typeof(object));
                context.Generator.StoreLocalValueFromStack(targetLocal);

                GenerateReadMethod(context);
                context.Generator.PushTypeOntoStack(type);
                context.Generator.PushLocalValueOntoStack(targetLocal);
                context.PushDeserializedObjectsCollectionOntoStack();
                pushObjectIdOntoStackAction();

                context.Generator.GenerateCodeCall <MethodInfo, Type, object, AutoResizingList <object>, int>(GenerateReadDelegateStaticHelper);
            });
        }
        protected override MethodInfo GenerateInner()
        {
            DynamicMethod dynamicMethod;

            if (type.IsArray)
            {
                dynamicMethod = new DynamicMethod("Read", returnType, parameterTypes, true);
            }
            else
            {
                dynamicMethod = new DynamicMethod("Read", returnType, parameterTypes, type, true);
            }
            var generator = dynamicMethod.GetILGenerator();
            var context   = new ReaderGenerationContext(generator, disableStamping, treatCollectionAsUserObject, OpCodes.Ldarg_2, OpCodes.Ldarg_0);

            GenerateDynamicCode(context, type);

#if DEBUG_FORMAT
            GeneratorHelper.DumpToLibrary <ReadMethodDelegate>(context, c => GenerateDynamicCode((ReaderGenerationContext)c, type));
#endif

            return(dynamicMethod);
        }
        private static void GenerateReadObjectInner(ReaderGenerationContext context, Type formalType)
        {
            var objectIdLocal = context.Generator.DeclareLocal(typeof(int));

            context.PushObjectIdOntoStack();
            context.Generator.StoreLocalValueFromStack(objectIdLocal);

            GenerateTouchObject(context, formalType);

            switch (ObjectReader.GetCreationWay(formalType, context.TreatCollectionAsUserObject))
            {
            case ObjectReader.CreationWay.Null:
                GenerateReadNotPrecreated(context, formalType, objectIdLocal);
                break;

            case ObjectReader.CreationWay.DefaultCtor:
                GenerateUpdateElements(context, formalType, objectIdLocal);
                break;

            case ObjectReader.CreationWay.Uninitialized:
                GenerateUpdateFields(context, formalType, objectIdLocal);
                break;
            }
        }
Example #17
0
        protected override void InnerGenerate(ReaderGenerationContext context)
        {
            var finish       = context.Generator.DefineLabel();
            var createdLocal = context.Generator.DeclareLocal(typeof(object));

            context.PushObjectIdOntoStack();
            context.PushDeserializedObjectsCollectionOntoStack();
            context.Generator.PushPropertyValueOntoStack <AutoResizingList <object>, int>(x => x.Count);
            context.Generator.Emit(OpCodes.Blt, finish);

            if (CreateObjectGenerator.TryFillBody(context.Generator, type, treatCollectionAsUserObject))
            {
                context.Generator.StoreLocalValueFromStack(createdLocal);
                context.PushObjectReaderOntoStack();
                context.PushObjectIdOntoStack();
                context.Generator.PushLocalValueOntoStack(createdLocal);
                context.Generator.Call <ObjectReader>(x => x.SetObjectByReferenceId(0, null));
            }
            else if (type.IsArray)
            {
                var isMultidimensional = type.GetArrayRank() > 1;
                var elementFormalType  = type.GetElementType();

                var rankLocal    = context.Generator.DeclareLocal(typeof(int));
                var lengthsLocal = isMultidimensional ? context.Generator.DeclareLocal(typeof(int[])) : context.Generator.DeclareLocal(typeof(int));

                context.PushObjectReaderOntoStack();
                context.PushObjectIdOntoStack();

                ReadMethodGenerator.GenerateReadPrimitive(context, typeof(int));
                context.Generator.StoreLocalValueFromStack(rankLocal);
                if (isMultidimensional)
                {
                    context.Generator.PushLocalValueOntoStack(rankLocal);
                    context.Generator.Emit(OpCodes.Newarr, typeof(int));
                    context.Generator.StoreLocalValueFromStack(lengthsLocal); // create an array for keeping the lengths of each dimension

                    GeneratorHelper.GenerateLoop(context, rankLocal, i =>
                    {
                        context.Generator.PushLocalValueOntoStack(lengthsLocal);
                        context.Generator.PushLocalValueOntoStack(i);
                        ReadMethodGenerator.GenerateReadPrimitive(context, typeof(int));
                        context.Generator.Emit(OpCodes.Stelem, typeof(int)); // populate the lengths with values read from stream
                    });
                }
                else
                {
                    ReadMethodGenerator.GenerateReadPrimitive(context, typeof(int));
                    context.Generator.StoreLocalValueFromStack(lengthsLocal);
                }

                context.Generator.PushTypeOntoStack(elementFormalType);
                context.Generator.PushLocalValueOntoStack(lengthsLocal);

                if (isMultidimensional)
                {
                    context.Generator.Call(() => Array.CreateInstance(null, new int[0]));
                }
                else
                {
                    context.Generator.Call(() => Array.CreateInstance(null, 0));
                }

                context.Generator.Call <ObjectReader>(x => x.SetObjectByReferenceId(0, null));
            }
            else if (type == typeof(string))
            {
                context.PushObjectReaderOntoStack();
                context.PushObjectIdOntoStack();
                ReadMethodGenerator.GenerateReadPrimitive(context, typeof(string));
                context.Generator.Call <ObjectReader>(x => x.SetObjectByReferenceId(0, null));

                var field = Helpers.GetFieldInfo <ObjectReader, int>(x => x.objectsWrittenInlineCount);

                context.PushObjectReaderOntoStack();
                context.Generator.Emit(OpCodes.Dup);
                context.Generator.Emit(OpCodes.Ldfld, field);
                context.Generator.PushIntegerOntoStack(1);
                context.Generator.Emit(OpCodes.Add);
                context.Generator.Emit(OpCodes.Stfld, field);
            }
            else
            {
                ReadMethodGenerator.GenerateReadNotPrecreated(context, type, context.PushObjectIdOntoStack);

                var field = Helpers.GetFieldInfo <ObjectReader, int>(x => x.objectsWrittenInlineCount);

                context.PushObjectReaderOntoStack();
                context.Generator.Emit(OpCodes.Dup);
                context.Generator.Emit(OpCodes.Ldfld, field);
                context.Generator.PushIntegerOntoStack(1);
                context.Generator.Emit(OpCodes.Add);
                context.Generator.Emit(OpCodes.Stfld, field);
            }

            context.Generator.MarkLabel(finish);
        }
 protected abstract void InnerGenerate(ReaderGenerationContext context);
        private void GenerateCallPostDeserializationHooks(ReaderGenerationContext context, int factoryId)
        {
            var methods = Helpers.GetMethodsWithAttribute(typeof(PostDeserializationAttribute), type).ToArray();

            foreach (var method in methods)
            {
                if (!method.IsStatic)
                {
                    context.PushDeserializedObjectOntoStack(context.PushObjectIdOntoStack);
                    context.Generator.Emit(OpCodes.Castclass, method.ReflectedType);
                }
                if (method.IsVirtual)
                {
                    context.Generator.Emit(OpCodes.Callvirt, method);
                }
                else
                {
                    context.Generator.Emit(OpCodes.Call, method);
                }
            }

            methods = Helpers.GetMethodsWithAttribute(typeof(LatePostDeserializationAttribute), type).ToArray();
            if (factoryId != -1 && methods.Length != 0)
            {
                throw new InvalidOperationException(
                          string.Format(ObjectReader.LateHookAndSurrogateError, type));
            }

            foreach (var method in methods)
            {
                context.PushObjectReaderOntoStack();
                context.Generator.PushFieldValueOntoStack <ObjectReader, List <Action> >(x => x.latePostDeserializationHooks);

                context.Generator.PushTypeOntoStack(typeof(Action));
                if (method.IsStatic)
                {
                    context.Generator.Emit(OpCodes.Ldnull);
                }
                else
                {
                    context.PushDeserializedObjectOntoStack(context.PushObjectIdOntoStack);
                    context.Generator.Emit(OpCodes.Castclass, method.ReflectedType);
                }

                context.Generator.Emit(OpCodes.Ldtoken, method);
                if (method.DeclaringType.IsGenericType)
                {
                    context.Generator.Emit(OpCodes.Ldtoken, method.DeclaringType);

                    context.Generator.Emit(OpCodes.Call, Helpers.GetMethodInfo <object, MethodBase>(x => MethodBase.GetMethodFromHandle(new RuntimeMethodHandle(), new RuntimeTypeHandle())));
                }
                else
                {
                    context.Generator.Emit(OpCodes.Call, Helpers.GetMethodInfo <object, MethodBase>(x => MethodBase.GetMethodFromHandle(new RuntimeMethodHandle())));
                }

                context.Generator.Emit(OpCodes.Castclass, typeof(MethodInfo));
                context.Generator.Emit(OpCodes.Call, Helpers.GetMethodInfo <object, Delegate>(x => Delegate.CreateDelegate(null, null, method)));

                context.Generator.Emit(OpCodes.Castclass, typeof(Action));

                context.Generator.Emit(OpCodes.Call, Helpers.GetMethodInfo <List <Action> >(x => x.Add(null)));
            }

            if (callPostDeserializationCallback)
            {
                context.PushObjectReaderOntoStack();
                context.Generator.PushFieldValueOntoStack <ObjectReader, Action <object> >(x => x.postDeserializationCallback);
                context.PushDeserializedObjectOntoStack(context.PushObjectIdOntoStack);
                context.Generator.Emit(OpCodes.Call, Helpers.GetMethodInfo <Action <object> >(x => x.Invoke(null)));
            }
        }
        private static void GenerateReadArray(ReaderGenerationContext context, Type arrayType, Action pushObjectIdOntoStackAction)
        {
            var isMultidimensional = arrayType.GetArrayRank() > 1;
            var elementFormalType  = arrayType.GetElementType();

            var rankLocal          = context.Generator.DeclareLocal(typeof(int));
            var lengthsLocal       = isMultidimensional ? context.Generator.DeclareLocal(typeof(int[])) : context.Generator.DeclareLocal(typeof(int));
            var arrayLocal         = context.Generator.DeclareLocal(typeof(Array));
            var positionLocal      = isMultidimensional ? context.Generator.DeclareLocal(typeof(int[])) : context.Generator.DeclareLocal(typeof(int));
            var loopControlLocal   = context.Generator.DeclareLocal(typeof(int)); // type is int not bool to reuse array length directly
            var loopBeginLabel     = context.Generator.DefineLabel();
            var loopEndLabel       = context.Generator.DefineLabel();
            var nonZeroLengthLabel = context.Generator.DefineLabel();

            context.PushDeserializedObjectOntoStack(pushObjectIdOntoStackAction);
            context.Generator.Emit(OpCodes.Castclass, typeof(Array));
            context.Generator.Emit(OpCodes.Dup);
            context.Generator.StoreLocalValueFromStack(arrayLocal);
            context.Generator.PushPropertyValueOntoStack <Array, int>(x => x.Rank);
            context.Generator.StoreLocalValueFromStack(rankLocal);

            if (isMultidimensional)
            {
                context.Generator.Emit(OpCodes.Ldc_I4_1);
                context.Generator.StoreLocalValueFromStack(loopControlLocal);

                context.Generator.PushLocalValueOntoStack(rankLocal);
                context.Generator.Emit(OpCodes.Newarr, typeof(int));
                context.Generator.StoreLocalValueFromStack(lengthsLocal); // create an array for keeping the lengths of each dimension

                GeneratorHelper.GenerateLoop(context, rankLocal, i =>
                {
                    context.Generator.PushLocalValueOntoStack(lengthsLocal);
                    context.Generator.PushLocalValueOntoStack(i);

                    context.Generator.PushLocalValueOntoStack(arrayLocal);
                    context.Generator.PushLocalValueOntoStack(i);
                    context.Generator.Call <Array>(x => x.GetLength(0));

                    context.Generator.Emit(OpCodes.Dup);
                    context.Generator.Emit(OpCodes.Brtrue, nonZeroLengthLabel);

                    context.Generator.Emit(OpCodes.Ldc_I4_0);
                    context.Generator.StoreLocalValueFromStack(loopControlLocal);

                    context.Generator.MarkLabel(nonZeroLengthLabel);
                    context.Generator.Emit(OpCodes.Stelem, typeof(int)); // populate the lengths with values read from stream
                });
            }
            else
            {
                context.Generator.PushLocalValueOntoStack(arrayLocal);
                context.Generator.Emit(OpCodes.Ldc_I4_0);
                context.Generator.Call <Array>(x => x.GetLength(0));
                context.Generator.Emit(OpCodes.Dup);
                context.Generator.StoreLocalValueFromStack(lengthsLocal);
                context.Generator.StoreLocalValueFromStack(loopControlLocal);
            }

            if (isMultidimensional)
            {
                context.Generator.PushLocalValueOntoStack(rankLocal);
                context.Generator.Emit(OpCodes.Newarr, typeof(int));
                context.Generator.StoreLocalValueFromStack(positionLocal); // create an array for keeping the current position of each dimension
            }

            context.Generator.MarkLabel(loopBeginLabel);
            context.Generator.PushLocalValueOntoStack(loopControlLocal);
            context.Generator.Emit(OpCodes.Brfalse, loopEndLabel);

            context.Generator.PushLocalValueOntoStack(arrayLocal);
            context.Generator.Emit(OpCodes.Castclass, arrayType);
            if (isMultidimensional)
            {
                GenerateReadField(context, elementFormalType);
                context.Generator.PushLocalValueOntoStack(positionLocal);
                context.Generator.Emit(OpCodes.Call, Helpers.GetMethodInfo <Array>(a => a.SetValue(null, new int[0])));
            }
            else
            {
                context.Generator.PushLocalValueOntoStack(positionLocal);
                context.Generator.Emit(OpCodes.Ldelema, elementFormalType);

                GenerateReadField(context, elementFormalType, false);
                context.Generator.Emit(OpCodes.Stobj, elementFormalType);
            }

            if (isMultidimensional)
            {
                context.Generator.PushLocalValueOntoStack(positionLocal);
                context.Generator.PushLocalValueOntoStack(lengthsLocal);
                context.Generator.PushLocalValueOntoStack(rankLocal);

                context.Generator.GenerateCodeFCall <int[], int[], int, bool>(GenerateReadArrayStaticHelper);
            }
            else
            {
                context.Generator.PushLocalValueOntoStack(positionLocal);
                context.Generator.Emit(OpCodes.Ldc_I4_1);
                context.Generator.Emit(OpCodes.Add);
                context.Generator.Emit(OpCodes.Dup);
                context.Generator.StoreLocalValueFromStack(positionLocal);
                context.Generator.PushLocalValueOntoStack(lengthsLocal);
                context.Generator.Emit(OpCodes.Clt);
            }

            context.Generator.StoreLocalValueFromStack(loopControlLocal);

            context.Generator.Emit(OpCodes.Br, loopBeginLabel);
            context.Generator.MarkLabel(loopEndLabel);
        }
 private static void GenerateDynamicCode(ReaderGenerationContext context, Type typeToGenerate)
 {
     GenerateReadObjectInner(context, typeToGenerate);
     context.Generator.Emit(OpCodes.Ret);
 }
        private static void GenerateReadField(ReaderGenerationContext context, Type formalType, bool boxIfValueType = true)
        {
            // method returns read field value on stack

            if (Helpers.IsTransient(formalType))
            {
                context.Generator.PushTypeOntoStack(formalType);
                context.Generator.Emit(OpCodes.Call, Helpers.GetMethodInfo <object, object>(x => Helpers.GetDefaultValue(null)));

                if (formalType.IsValueType && boxIfValueType)
                {
                    context.Generator.Emit(OpCodes.Box, formalType);
                }
                return; //return Helpers.GetDefaultValue(_formalType);
            }

            var finishLabel = context.Generator.DefineLabel();

            if (!formalType.IsValueType)
            {
                var referenceIdLocal             = context.Generator.DeclareLocal(typeof(int));
                var returnDeserializeObjectLabel = context.Generator.DefineLabel();

                context.PushObjectReaderOntoStack();
                context.Generator.Call <ObjectReader>(x => x.ReadAndTouchReference());

                context.Generator.Emit(OpCodes.Dup);
                context.Generator.StoreLocalValueFromStack(referenceIdLocal);
                context.Generator.Emit(OpCodes.Ldc_I4, Consts.NullObjectId);
                context.Generator.Emit(OpCodes.Bne_Un, returnDeserializeObjectLabel);

                context.Generator.Emit(OpCodes.Ldnull);
                context.Generator.Emit(OpCodes.Br, finishLabel);

                context.Generator.MarkLabel(returnDeserializeObjectLabel);

                context.PushDeserializedObjectOntoStack(referenceIdLocal, true);
                context.Generator.Emit(OpCodes.Castclass, formalType);

                context.Generator.MarkLabel(finishLabel);
                return;
            }

            var continueWithNullableLabel = context.Generator.DefineLabel();
            var forcedFormalType          = formalType;
            var forcedBoxIfValueType      = boxIfValueType;

            var nullableActualType = Nullable.GetUnderlyingType(formalType);

            if (nullableActualType != null)
            {
                forcedFormalType     = nullableActualType;
                forcedBoxIfValueType = true;

                GenerateReadPrimitive(context, typeof(bool));
                context.Generator.Emit(OpCodes.Brtrue, continueWithNullableLabel);

                context.Generator.Emit(OpCodes.Ldnull);
                context.Generator.Emit(OpCodes.Br, finishLabel);

                context.Generator.MarkLabel(continueWithNullableLabel);
            }

            if (forcedFormalType.IsEnum)
            {
                var actualType = Enum.GetUnderlyingType(forcedFormalType);

                context.Generator.PushTypeOntoStack(forcedFormalType);
                GenerateReadPrimitive(context, actualType);
                context.Generator.Emit(OpCodes.Call, typeof(Enum).GetMethod("ToObject", BindingFlags.Static | BindingFlags.Public, null, new[] {
                    typeof(Type),
                    actualType
                }, null));

                if (!forcedBoxIfValueType)
                {
                    context.Generator.Emit(OpCodes.Unbox_Any, forcedFormalType);
                }
            }
            else if (Helpers.IsWriteableByPrimitiveWriter(forcedFormalType))
            {
                // value type
                GenerateReadPrimitive(context, forcedFormalType);

                if (forcedBoxIfValueType)
                {
                    context.Generator.Emit(OpCodes.Box, forcedFormalType);
                }
            }
            else
            {
                // here we have struct
                var structLocal = context.Generator.DeclareLocal(forcedFormalType);
                GenerateUpdateStructFields(context, forcedFormalType, structLocal);
                context.Generator.PushLocalValueOntoStack(structLocal);
                if (forcedBoxIfValueType)
                {
                    context.Generator.Emit(OpCodes.Box, forcedFormalType);
                }
            }

            context.Generator.MarkLabel(finishLabel);

            // if the value is nullable we must use special initialization of it
            if (nullableActualType != null)
            {
                var nullableLocal           = context.Generator.DeclareLocal(formalType);
                var returnNullNullableLabel = context.Generator.DefineLabel();
                var endLabel = context.Generator.DefineLabel();

                context.Generator.Emit(OpCodes.Dup);
                context.Generator.Emit(OpCodes.Brfalse, returnNullNullableLabel);

                if (forcedBoxIfValueType)
                {
                    context.Generator.Emit(OpCodes.Unbox_Any, nullableActualType);
                }
                context.Generator.Emit(OpCodes.Newobj, formalType.GetConstructor(new[] { nullableActualType }));
                context.Generator.StoreLocalValueFromStack(nullableLocal);
                context.Generator.PushLocalValueOntoStack(nullableLocal);

                if (boxIfValueType)
                {
                    context.Generator.Emit(OpCodes.Box, formalType);
                }

                context.Generator.Emit(OpCodes.Br, endLabel);

                context.Generator.MarkLabel(returnNullNullableLabel);
                context.Generator.Emit(OpCodes.Pop);
                context.Generator.PushLocalAddressOntoStack(nullableLocal);
                context.Generator.Emit(OpCodes.Initobj, formalType);

                context.Generator.PushLocalValueOntoStack(nullableLocal);
                if (boxIfValueType)
                {
                    context.Generator.Emit(OpCodes.Box, nullableLocal);
                }

                context.Generator.MarkLabel(endLabel);
            }
        }
 private static void GenerateReadNotPrecreated(ReaderGenerationContext context, Type formalType, LocalBuilder objectIdLocal)
 {
     GenerateReadNotPrecreated(context, formalType, () => context.Generator.PushLocalValueOntoStack(objectIdLocal));
 }
        private static void GenerateFillCollection(ReaderGenerationContext context, CollectionMetaToken collectionToken, Type collectionType, LocalBuilder objectIdLocal)
        {
            var countLocal = context.Generator.DeclareLocal(typeof(int));

            GenerateReadPrimitive(context, typeof(int));
            context.Generator.StoreLocalValueFromStack(countLocal); // read collection elements count
            var addMethod = collectionToken.AddMethod;

            var elementFormalType = collectionToken.FormalElementType;

            if (collectionType == typeof(Stack) ||
                (collectionType.IsGenericType && collectionType.GetGenericTypeDefinition() == typeof(Stack <>)))
            {
                var tempArrLocal = context.Generator.DeclareLocal(elementFormalType.MakeArrayType());

                context.Generator.PushLocalValueOntoStack(countLocal);
                context.Generator.Emit(OpCodes.Newarr, elementFormalType);
                context.Generator.StoreLocalValueFromStack(tempArrLocal); // creates temporal array

                GeneratorHelper.GenerateLoop(context, countLocal, cl =>
                {
                    context.Generator.PushLocalValueOntoStack(tempArrLocal);
                    context.Generator.PushLocalValueOntoStack(cl);
                    GenerateReadField(context, elementFormalType, false);
                    context.Generator.Emit(OpCodes.Stelem, elementFormalType);
                });

                GeneratorHelper.GenerateLoop(context, countLocal, cl =>
                {
                    context.PushDeserializedObjectOntoStack(objectIdLocal);
                    context.Generator.Emit(OpCodes.Castclass, collectionType);

                    context.Generator.PushLocalValueOntoStack(tempArrLocal);
                    context.Generator.PushLocalValueOntoStack(cl);
                    context.Generator.Emit(OpCodes.Ldelem, elementFormalType);
                    context.Generator.Emit(OpCodes.Callvirt, addMethod);
                }, true);
            }
            else
            {
                GeneratorHelper.GenerateLoop(context, countLocal, cl =>
                {
                    context.PushDeserializedObjectOntoStack(objectIdLocal);
                    context.Generator.Emit(OpCodes.Castclass, collectionType);

                    var currentIdLocal = default(LocalBuilder);
                    var debtDifference = default(LocalBuilder);
                    var valueLocal     = default(LocalBuilder);

                    if (collectionToken.IsHashCodeBased)
                    {
                        currentIdLocal = context.Generator.DeclareLocal(typeof(int));
                        debtDifference = context.Generator.DeclareLocal(typeof(int));
                    }

                    if (collectionToken.IsDictionary)
                    {
                        valueLocal = context.Generator.DeclareLocal(collectionToken.FormalValueType);
                    }

                    if (collectionToken.IsHashCodeBased)
                    {
                        context.PushObjectReaderOntoStack();
                        context.Generator.Call <ObjectReader>(or => or.GetNewestObjectId());
                        context.Generator.StoreLocalValueFromStack(currentIdLocal);

                        context.PushObjectReaderOntoStack();
                        context.Generator.Call <ObjectReader>(or => or.GetObjectDebt());
                        context.Generator.StoreLocalValueFromStack(debtDifference);
                    }

                    GenerateReadField(context, collectionToken.IsDictionary ? collectionToken.FormalKeyType : collectionToken.FormalElementType, false);

                    if (collectionToken.IsHashCodeBased)
                    {
                        context.PushObjectReaderOntoStack();
                        context.Generator.Call <ObjectReader>(or => or.GetObjectDebt());

                        // Here we have new debt on top of the stack but need to load old one.
                        context.Generator.PushLocalValueOntoStack(debtDifference);
                        context.Generator.Emit(OpCodes.Sub);
                        context.Generator.StoreLocalValueFromStack(debtDifference);
                    }

                    if (collectionToken.IsDictionary)
                    {
                        GenerateReadField(context, collectionToken.FormalValueType, false);
                    }

                    if (collectionToken.IsHashCodeBased)
                    {
                        var deferredAddLabel = context.Generator.DefineLabel();
                        var exitLabel        = context.Generator.DefineLabel();
                        context.Generator.PushLocalValueOntoStack(debtDifference);
                        context.Generator.Emit(OpCodes.Brtrue, deferredAddLabel);

                        context.Generator.Emit(OpCodes.Callvirt, addMethod);
                        context.Generator.Emit(OpCodes.Br, exitLabel);

                        context.Generator.MarkLabel(deferredAddLabel);

                        if (collectionToken.IsDictionary)
                        {
                            context.Generator.StoreLocalValueFromStack(valueLocal);
                        }

                        context.Generator.Emit(OpCodes.Pop); // Key (or value if not dictionary)
                        context.Generator.Emit(OpCodes.Pop); // Collection (object)
                        context.PushObjectReaderOntoStack();
                        context.PushDeserializedObjectOntoStack(objectIdLocal);
                        context.Generator.PushLocalValueOntoStack(currentIdLocal);
                        context.Generator.Emit(OpCodes.Ldc_I4_1);
                        context.Generator.Emit(OpCodes.Add);
                        if (collectionToken.IsDictionary)
                        {
                            context.Generator.PushLocalValueOntoStack(valueLocal);
                        }
                        else
                        {
                            context.Generator.Emit(OpCodes.Ldnull);
                        }

                        context.Generator.Call <ObjectReader>(or => or.AddHashCodeBasedWaitingValue(null, 0, null));

                        context.Generator.MarkLabel(exitLabel);
                    }
                    else
                    {
                        context.Generator.Emit(OpCodes.Callvirt, addMethod);
                    }

                    if (addMethod.ReturnType != typeof(void))
                    {
                        context.Generator.Emit(OpCodes.Pop); // remove returned unused value from stack
                    }
                });
            }
        }