private void WriteSerializers(CompilerOptions options, string assemblyName, TypeBuilder type, bool root, SerializerPair[] basicMethodPairs, out int index, out bool hasInheritance, out SerializerPair[] methodPairs, out Compiler.CompilerContext.ILVersion ilVersion)
        {
            Compiler.CompilerContext ctx;

            index          = 0;
            hasInheritance = false;
            methodPairs    = new SerializerPair[_types.Count];
            foreach (MetaType metaType in _types)
            {
                string writeName = "Write";
                if (root)
                {
                    writeName += "Root";
                }
#if DEBUG
                writeName += metaType.Type.Name;
#endif

                MethodContext writeGenCtx;
                MethodBuilder writeMethod = EmitDefineMethod(
                    type,
                    writeName,
                    MethodAttributes.Private | MethodAttributes.Static,
                    CallingConventions.Standard,
                    MapType(typeof(void)),
                    new[]
                {
                    new MethodContext.ParameterGenInfo(metaType.Type, "obj", 1),
                    new MethodContext.ParameterGenInfo(MapType(typeof(ProtoWriter)), "dest", 2),
                },
                    false,
                    out writeGenCtx);


                string readName = "Read";
                if (root)
                {
                    readName += "Root";
                }
#if DEBUG
                readName += metaType.Type.Name;
#endif
                MethodContext readGenCtx;
                MethodBuilder readMethod = EmitDefineMethod(
                    type,
                    readName,
                    MethodAttributes.Private | MethodAttributes.Static,
                    CallingConventions.Standard,
                    metaType.Type,
                    new[]
                {
                    new MethodContext.ParameterGenInfo(metaType.Type, "obj", 1),
                    new MethodContext.ParameterGenInfo(MapType(typeof(ProtoReader)), "source", 2),
                },
                    false,
                    out readGenCtx);

                SerializerPair pair = new SerializerPair(
                    GetKey(metaType.Type, true, false), GetKey(metaType.Type, true, true), metaType,
                    writeMethod, readMethod, writeGenCtx, readGenCtx);
                methodPairs[index++] = pair;
                if (pair.MetaKey != pair.BaseKey)
                {
                    hasInheritance = true;
                }
            }

            if (hasInheritance)
            {
                Array.Sort(methodPairs);
            }

            ilVersion = Compiler.CompilerContext.ILVersion.Net2;
            if (options.MetaDataVersion == 0x10000)
            {
                ilVersion = Compiler.CompilerContext.ILVersion.Net1; // old-school!
            }
            for (index = 0; index < methodPairs.Length; index++)
            {
                SerializerPair pair = methodPairs[index];

                var ser = root ? pair.Type.RootSerializer : pair.Type.Serializer;

                ctx = new Compiler.CompilerContext(pair.SerializeBody, true, true, basicMethodPairs ?? methodPairs, this, ilVersion, assemblyName, pair.Type.Type);
                ctx.CheckAccessibility(pair.Deserialize.ReturnType);
                ser.EmitWrite(ctx, ctx.InputValue);
                ctx.Return();

                ctx = new Compiler.CompilerContext(pair.DeserializeBody, true, false, basicMethodPairs ?? methodPairs, this, ilVersion, assemblyName, pair.Type.Type);
                ser.EmitRead(ctx, ctx.InputValue);
                if (!ser.EmitReadReturnsValue)
                {
                    ctx.LoadValue(ctx.InputValue);
                }
                ctx.Return();
            }
        }
        private Compiler.CompilerContext WriteSerializeDeserialize(string assemblyName, TypeBuilder type, SerializerPair[] methodPairs, SerializerPair[] rootMethodPairs, Compiler.CompilerContext.ILVersion ilVersion, ref ILGenerator il)
        {
            Compiler.CompilerContext ctx;
            // arg0 = this, arg1 = key, arg2=obj, arg3=dest
            Compiler.CodeLabel[] jumpTable;
            {
                var genInfo = Override(type, "Serialize");
                il        = genInfo.GetILGenerator();
                ctx       = new Compiler.CompilerContext(genInfo, false, true, methodPairs, this, ilVersion, assemblyName, MapType(typeof(object)));
                jumpTable = new Compiler.CodeLabel[_types.Count];
                for (int i = 0; i < jumpTable.Length; i++)
                {
                    jumpTable[i] = ctx.DefineLabel();
                }
                il.Emit(OpCodes.Ldarg_1);
                ctx.Switch(jumpTable);
                ctx.Return();
                for (int i = 0; i < jumpTable.Length; i++)
                {
                    SerializerPair pair = methodPairs[i];
                    ctx.MarkLabel(jumpTable[i]);
                    var labelNoRoot = ctx.DefineLabel();

                    il.Emit(OpCodes.Ldarg_S, 4);
                    ctx.BranchIfFalse(labelNoRoot, true);

                    WriteSerializePair(il, ctx, rootMethodPairs[i]);

                    ctx.MarkLabel(labelNoRoot);

                    WriteSerializePair(il, ctx, pair);
                }
            }

            {
                var genInfo = Override(type, "Deserialize");
                il  = genInfo.GetILGenerator();
                ctx = new Compiler.CompilerContext(genInfo, false, false, methodPairs, this, ilVersion, assemblyName, MapType(typeof(object)));
                // arg0 = this, arg1 = key, arg2=obj, arg3=source
                for (int i = 0; i < jumpTable.Length; i++)
                {
                    jumpTable[i] = ctx.DefineLabel();
                }
                il.Emit(OpCodes.Ldarg_1);
                ctx.Switch(jumpTable);
                ctx.LoadNullRef();
                ctx.Return();
                for (int i = 0; i < jumpTable.Length; i++)
                {
                    SerializerPair pair = methodPairs[i];
                    ctx.MarkLabel(jumpTable[i]);
                    var labelNoRoot = ctx.DefineLabel();

                    il.Emit(OpCodes.Ldarg_S, 4);
                    ctx.BranchIfFalse(labelNoRoot, true);

                    WriteDeserializePair(assemblyName, type, rootMethodPairs, ilVersion, il, rootMethodPairs[i], i, ctx);

                    ctx.MarkLabel(labelNoRoot);

                    WriteDeserializePair(assemblyName, type, methodPairs, ilVersion, il, pair, i, ctx);
                }
            }
            return(ctx);
        }
        private void WriteGetKeyImpl(TypeBuilder type, bool hasInheritance, SerializerPair[] methodPairs, Compiler.CompilerContext.ILVersion ilVersion, string assemblyName, out ILGenerator il, out int knownTypesCategory, out FieldBuilder knownTypes, out Type knownTypesLookupType)
        {
            var genInfo = Override(type, "GetKeyImpl");

            il = genInfo.GetILGenerator();
            Compiler.CompilerContext ctx = new Compiler.CompilerContext(genInfo, false, false, methodPairs, this, ilVersion, assemblyName, MapType(typeof(System.Type), true));

            if (_types.Count <= KnownTypes_ArrayCutoff)
            {
                knownTypesCategory   = KnownTypes_Array;
                knownTypesLookupType = MapType(typeof(System.Type[]), true);
            }
            else
            {
#if NO_GENERICS
                knownTypesLookupType = null;
#else
                knownTypesLookupType = MapType(typeof(System.Collections.Generic.Dictionary <System.Type, int>), false);
#endif
                if (knownTypesLookupType == null)
                {
                    knownTypesLookupType = MapType(typeof(Hashtable), true);
                    knownTypesCategory   = KnownTypes_Hashtable;
                }
                else
                {
                    knownTypesCategory = KnownTypes_Dictionary;
                }
            }
            knownTypes = type.DefineField("knownTypes", knownTypesLookupType, FieldAttributes.Private | FieldAttributes.InitOnly | FieldAttributes.Static);

            switch (knownTypesCategory)
            {
            case KnownTypes_Array:
            {
                il.Emit(OpCodes.Ldsfld, knownTypes);
                il.Emit(OpCodes.Ldarg_1);
                // note that Array.IndexOf is not supported under CF
                il.EmitCall(OpCodes.Callvirt, MapType(typeof(IList)).GetMethod(
                                "IndexOf", new Type[] { MapType(typeof(object)) }), null);
                if (hasInheritance)
                {
                    il.DeclareLocal(MapType(typeof(int)));         // loc-0
                    il.Emit(OpCodes.Dup);
                    il.Emit(OpCodes.Stloc_0);

                    BasicList getKeyLabels = new BasicList();
                    int       lastKey      = -1;
                    for (int i = 0; i < methodPairs.Length; i++)
                    {
                        if (methodPairs[i].MetaKey == methodPairs[i].BaseKey)
                        {
                            break;
                        }
                        if (lastKey == methodPairs[i].BaseKey)
                        {           // add the last label again
                            getKeyLabels.Add(getKeyLabels[getKeyLabels.Count - 1]);
                        }
                        else
                        {           // add a new unique label
                            getKeyLabels.Add(ctx.DefineLabel());
                            lastKey = methodPairs[i].BaseKey;
                        }
                    }
                    Compiler.CodeLabel[] subtypeLabels = new Compiler.CodeLabel[getKeyLabels.Count];
                    getKeyLabels.CopyTo(subtypeLabels, 0);

                    ctx.Switch(subtypeLabels);
                    il.Emit(OpCodes.Ldloc_0);         // not a sub-type; use the original value
                    il.Emit(OpCodes.Ret);

                    lastKey = -1;
                    // now output the different branches per sub-type (not derived type)
                    for (int i = subtypeLabels.Length - 1; i >= 0; i--)
                    {
                        if (lastKey != methodPairs[i].BaseKey)
                        {
                            lastKey = methodPairs[i].BaseKey;
                            // find the actual base-index for this base-key (i.e. the index of
                            // the base-type)
                            int keyIndex = -1;
                            for (int j = subtypeLabels.Length; j < methodPairs.Length; j++)
                            {
                                if (methodPairs[j].BaseKey == lastKey && methodPairs[j].MetaKey == lastKey)
                                {
                                    keyIndex = j;
                                    break;
                                }
                            }
                            ctx.MarkLabel(subtypeLabels[i]);
                            Compiler.CompilerContext.LoadValue(il, keyIndex);
                            il.Emit(OpCodes.Ret);
                        }
                    }
                }
                else
                {
                    il.Emit(OpCodes.Ret);
                }
            }
            break;

            case KnownTypes_Dictionary:
            {
                LocalBuilder result    = il.DeclareLocal(MapType(typeof(int)));
                Label        otherwise = il.DefineLabel();
                il.Emit(OpCodes.Ldsfld, knownTypes);
                il.Emit(OpCodes.Ldarg_1);
                il.Emit(OpCodes.Ldloca_S, result);
                il.EmitCall(OpCodes.Callvirt, knownTypesLookupType.GetMethod("TryGetValue", BindingFlags.Instance | BindingFlags.Public), null);
                il.Emit(OpCodes.Brfalse_S, otherwise);
                il.Emit(OpCodes.Ldloc_S, result);
                il.Emit(OpCodes.Ret);
                il.MarkLabel(otherwise);
                il.Emit(OpCodes.Ldc_I4_M1);
                il.Emit(OpCodes.Ret);
            }
            break;

            case KnownTypes_Hashtable:
            {
                Label otherwise = il.DefineLabel();
                il.Emit(OpCodes.Ldsfld, knownTypes);
                il.Emit(OpCodes.Ldarg_1);
                il.EmitCall(OpCodes.Callvirt, knownTypesLookupType.GetProperty("Item").GetGetMethod(), null);
                il.Emit(OpCodes.Dup);
                il.Emit(OpCodes.Brfalse_S, otherwise);
#if FX11
                il.Emit(OpCodes.Unbox, MapType(typeof(int)));
                il.Emit(OpCodes.Ldobj, MapType(typeof(int)));
#else
                if (ilVersion == Compiler.CompilerContext.ILVersion.Net1)
                {
                    il.Emit(OpCodes.Unbox, MapType(typeof(int)));
                    il.Emit(OpCodes.Ldobj, MapType(typeof(int)));
                }
                else
                {
                    il.Emit(OpCodes.Unbox_Any, MapType(typeof(int)));
                }
#endif
                il.Emit(OpCodes.Ret);
                il.MarkLabel(otherwise);
                il.Emit(OpCodes.Pop);
                il.Emit(OpCodes.Ldc_I4_M1);
                il.Emit(OpCodes.Ret);
            }
            break;

            default:
                throw new InvalidOperationException();
            }
        }
        private static MethodContext EmitBoxedSerializer(TypeBuilder type, int i, Type valueType, SerializerPair[] methodPairs, RuntimeTypeModel model, Compiler.CompilerContext.ILVersion ilVersion, string assemblyName)
        {
            MethodInfo dedicated = methodPairs[i].Deserialize;
            string     name      = "_" + i.ToString();

            MethodContext methodContext;

            model.EmitDefineMethod(
                type,
                name,
                MethodAttributes.Static,
                CallingConventions.Standard,
                model.MapType(typeof(object)),
                new[]
            {
                new MethodContext.ParameterGenInfo(model.MapType(typeof(object)), "obj", 1),
                new MethodContext.ParameterGenInfo(model.MapType(typeof(ProtoReader)), "source", 2),
            },
                false,
                out methodContext);

            Compiler.CompilerContext ctx = new Compiler.CompilerContext(methodContext, true, false, methodPairs, model, ilVersion, assemblyName, model.MapType(typeof(object)));
            ctx.LoadValue(ctx.InputValue);
            Compiler.CodeLabel @null = ctx.DefineLabel();
            ctx.BranchIfFalse(@null, true);

            Type mappedValueType = valueType;

            ctx.LoadValue(ctx.InputValue);
            ctx.CastFromObject(mappedValueType);
            ctx.LoadReaderWriter();
            ctx.EmitCall(dedicated);
            ctx.CastToObject(mappedValueType);
            ctx.Return();

            ctx.MarkLabel(@null);
            using (Compiler.Local typedVal = new Compiler.Local(ctx, mappedValueType))
            {
                // create a new valueType
                ctx.LoadAddress(typedVal, mappedValueType);
                ctx.EmitCtor(mappedValueType);
                ctx.LoadValue(typedVal);
                ctx.LoadReaderWriter();
                ctx.EmitCall(dedicated);
                ctx.CastToObject(mappedValueType);
                ctx.Return();
            }
            return(methodContext);
        }