void CreateDeserializationMethod(List <PropertySerializationInfo> properties)
        {
            const string HasNameTableLabel     = "_hasNameTable";
            const string endLabel              = "exitFunction";
            const string SkipPropertyLabel     = "_skipProperty";
            const string BaseSucceededLabel    = "_baseSucceeded";
            const string SkipLegacyDeserialize = "_skipLegacyDeserialize";

            int version = 0;

            PropertySerializationInfo.GenerateArgs args = new PropertySerializationInfo.GenerateArgs();

            args.il = new DynamicMethodHelper(
                "Deserialize_" + type.Name,
                null,
                new Type[] { typeof(object), typeof(int), typeof(TypeSerializationArgs) },
                this.type
                );

            args.instanceArg = 0;
            args.versionArg  = 1;
            args.dataArg     = 2;
            args.streamVar   = "_reader";
            args.headerVar   = "_header";

            args.il.DeclareLocal(args.streamVar, typeof(IPrimitiveReader));
            args.il.GetField(args.dataArg, typeof(TypeSerializationArgs).GetField("Reader"));
            args.il.PopLocal(args.streamVar);

            args.il.DeclareLocal(args.headerVar, typeof(TypeSerializationHeader));
            args.il.GetField(args.dataArg, typeof(TypeSerializationArgs).GetField("Header"));
            args.il.PopLocal(args.headerVar);

            //  Handle legacy deserialization
            if ((this.LegacyVersion > 0) && (this.MinVersion <= this.LegacyVersion))
            {
                MethodInfo method = this.Type.GetMethod(
                    "Deserialize",
                    BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
                    null,
                    new Type[] { typeof(IPrimitiveReader), typeof(int) },
                    null
                    );

                if (method == null)
                {
                    throw new ApplicationException(string.Format(
                                                       "Class {0} specified a legacy version but could not find the Deserialize method",
                                                       this.Type.Name
                                                       ));
                }

                //
                //  if (dataVersion > legacyVersion)
                //    goto SkipLegacyDeserialize
                //
                //      call instance.Deserialize(reader, version)
                //      goto endLabel
                //
                //  :SkipLegacyDeserialize
                //
                args.il.PushArg(args.versionArg);
                args.il.PushInt(this.LegacyVersion);
                args.il.GotoIfGreater(SkipLegacyDeserialize);

                args.il.PushArg(args.instanceArg);
                args.il.PushLocal(args.streamVar);
                args.il.PushArg(args.versionArg);
                args.il.CallMethod(method);

                args.il.Goto(endLabel);
                args.il.MarkLabel(SkipLegacyDeserialize);
            }

            //  TypeNameTable _typeNameTable = dataArg.NameTable;
            //  if (_typeNameType == null)
            //      _typeNameType = new TypeNameTable();
            //      dataArg.NameTable = _typeNameType;
            args.nameTableVar = "_typeNameTable";
            args.il.DeclareLocal(args.nameTableVar, typeof(TypeNameTable));
            args.il.GetField(args.dataArg, typeof(TypeSerializationArgs).GetField("NameTable"));
            args.il.PopLocal(args.nameTableVar);

            args.il.PushLocal(args.nameTableVar);
            args.il.GotoIfTrue(HasNameTableLabel);

            args.il.NewObject(args.nameTableVar);
            args.il.SetField(args.dataArg, args.nameTableVar, typeof(TypeSerializationArgs).GetField("NameTable"));

            args.il.BeginCallMethod(args.nameTableVar, "Deserialize", new Type[] { typeof(IPrimitiveReader), typeof(TypeSerializationHeader) });
            args.il.PushLocal(args.streamVar);
            args.il.PushLocal(args.headerVar);
            args.il.CallMethod();

            args.il.BeginCallMethod(args.nameTableVar, "GetSerializationInfo", new Type[] { typeof(SerializationInfo), typeof(int) });
            args.il.GetField(args.dataArg, typeof(TypeSerializationArgs).GetField("SerializationInfo"));
            args.il.PushInt(this.CurrentVersion);
            args.il.CallMethod();

            args.il.MarkLabel(HasNameTableLabel);

            //  If the type automatically serializes its base class
            //  then read it now. This must be done after the derived class
            //  has a chance to load the name table
            if (this.serializableBaseType != null)
            {
                FieldInfo fi = typeof(TypeSerializationArgs).GetField("IsBaseClass");

                args.il.PushArg(args.dataArg);
                args.il.PushBool(true);
                args.il.SetField(fi);

                //  Set IsBaseClass field to ensure proper type mapping
                args.il.PushArgAsRef(args.instanceArg);
                args.il.PushArg(args.dataArg);
                args.il.CallMethod(PropertySerializationInfo.GetTypeMethod(this.serializableBaseType, PropertySerializationInfo.TypeMethodType.Deserialize));

                args.il.GotoIfTrue(BaseSucceededLabel);

                args.il.PushArg(args.dataArg);
                args.il.PushBool(false);
                args.il.SetField(typeof(TypeSerializationArgs).GetField("Succeeded"));
                args.il.Goto(endLabel);

                args.il.MarkLabel(BaseSucceededLabel);

                //  Clear IsBaseClass flag
                args.il.PushArg(args.dataArg);
                args.il.PushBool(false);
                args.il.SetField(fi);
            }


            //  Create calls to deserialize each member
            foreach (PropertySerializationInfo propInfo in properties)
            {
                //  If we hit a higher version property then put in
                //  an "if" statement to exit if the serialized version
                //  is lower than the next batch of properties
                if (propInfo.Version > version)
                {
                    //  push versionParam
                    //  push propInfo.Version
                    //  branch_if_less_than endLabel    ; versionParam < propInfo.Version
                    args.il.PushArg(args.versionArg);
                    args.il.PushInt(propInfo.Version);
                    args.il.GotoIfLess(endLabel);

                    version = propInfo.Version;
                }

                args.il.BeginScope();

                //  If this property is marked obsolete, do not deserialize
                //  it if the data min version is equal to or greater than
                //  the version it was made obsolete in.
                if (propInfo.ObsoleteVersion > 0)
                {
                    const string NoHeaderLabel    = "_checkObsoleteNoHeader";
                    const string NotObsoleteLabel = "_notObsolete";

                    args.il.PushLocal(args.headerVar);
                    args.il.GotoIfFalse(NoHeaderLabel);

                    args.il.CallMethod(args.headerVar, typeof(TypeSerializationHeader).GetProperty("DataMinVersion").GetGetMethod());
                    args.il.PushInt(propInfo.ObsoleteVersion);
                    args.il.GotoIfGreaterOrEqual(SkipPropertyLabel);
                    args.il.Goto(NotObsoleteLabel);

                    args.il.MarkLabel(NoHeaderLabel);

                    args.il.PushArg(args.versionArg);
                    args.il.PushInt(propInfo.ObsoleteVersion);
                    args.il.GotoIfGreaterOrEqual(SkipPropertyLabel);

                    args.il.MarkLabel(NotObsoleteLabel);
                }

                propInfo.GenerateReadIL(args);
                args.il.MarkLabel(SkipPropertyLabel);
                args.il.EndScope();
            }

            //  :endFunction
            args.il.MarkLabel(endLabel);
            args.il.Return();

            this.autoDeserializeMethod = (AutoDeserializeMethod)args.il.Compile(typeof(AutoDeserializeMethod));
            this.deserializeMethod     = DeserializeAutoSerializable;
        }
        void CreateSerializationMethod(List <PropertySerializationInfo> properties)
        {
            PropertySerializationInfo.GenerateArgs args = new PropertySerializationInfo.GenerateArgs();
            FieldInfo nameTableField = typeof(TypeSerializationArgs).GetField("NameTable");

            const string CreatedNameTableVar         = "_createdNameTable";
            const string HasNameTableLabel           = "_hasNameTable";
            const string SkipNameTableSerializeLabel = "_skipNameTableSerialize";
            const string SerializationInfoVar        = "_serializationInfo";
            const string SkipUnhandledDataLabel      = "_skipUnhandledData";

            args.il = new DynamicMethodHelper(
                "Serialize_" + type.Name,
                null,
                new Type[] { typeof(object), typeof(TypeSerializationArgs) },
                this.type
                );

            args.instanceArg = 0;
            args.dataArg     = 1;
            args.streamVar   = "_writer";
            args.headerVar   = "_header";

            //  IPrimitiveWriter writer = dataArg.Writer;
            args.il.DeclareLocal(args.streamVar, typeof(IPrimitiveWriter));
            args.il.GetField(args.dataArg, typeof(TypeSerializationArgs).GetField("Writer"));
            args.il.PopLocal(args.streamVar);

            //  TypeSerializationHeader _header = dataArg.Header
            args.il.DeclareLocal(args.headerVar, typeof(TypeSerializationHeader));
            args.il.GetField(args.dataArg, typeof(TypeSerializationArgs).GetField("Header"));
            args.il.PopLocal(args.headerVar);

            //  SerializationInfo _serializationInfo = dataArg.SerializationInfo
            args.il.DeclareLocal(SerializationInfoVar, typeof(SerializationInfo));
            args.il.GetField(args.dataArg, typeof(TypeSerializationArgs).GetField("SerializationInfo"));
            args.il.PopLocal(SerializationInfoVar);

            //  bool createdNameTable
            //  TypeNameTable _typeNameTable = dataArg.NameTable;
            //
            //  if (_typeNameType == null)
            //  {
            //      _typeNameType = new TypeNameTable();
            //      dataArg.NameTable = _typeNameType;
            //      createdNameTable = true;
            //  }
            args.il.DeclareLocal(CreatedNameTableVar, typeof(bool));

            args.nameTableVar = "_typeNameTable";
            args.il.DeclareLocal(args.nameTableVar, typeof(TypeNameTable));
            args.il.GetField(args.dataArg, nameTableField);
            args.il.PopLocal(args.nameTableVar);

            args.il.PushLocal(args.nameTableVar);
            args.il.GotoIfTrue(HasNameTableLabel);

            args.il.PushBool(true);
            args.il.PopLocal(CreatedNameTableVar);

            args.il.NewObject(args.nameTableVar);
            args.il.SetField(args.dataArg, args.nameTableVar, nameTableField);

            args.il.BeginCallMethod(args.nameTableVar, "Add", new Type[] { typeof(SerializationInfo) });
            args.il.PushLocal(SerializationInfoVar);
            args.il.CallMethod();

            args.il.MarkLabel(HasNameTableLabel);

            //  If the type automatically serializes its base class
            //  then write it now. This must be done after the derived class
            //  has a chance to create the name table
            if (this.serializableBaseType != null)
            {
                FieldInfo fi = typeof(TypeSerializationArgs).GetField("IsBaseClass");

                args.il.PushArg(args.dataArg);
                args.il.PushBool(true);
                args.il.SetField(fi);

                args.il.PushArg(args.instanceArg);
                args.il.PushArg(args.dataArg);
                args.il.CallMethod(PropertySerializationInfo.GetTypeMethod(this.serializableBaseType, PropertySerializationInfo.TypeMethodType.Serialize));

                args.il.PushArg(args.dataArg);
                args.il.PushBool(false);
                args.il.SetField(fi);
            }

            //  Create calls to serialize each member
            foreach (PropertySerializationInfo propInfo in properties)
            {
                //  Don't serialize this property if it's marked obsolete
                //  and the min version is equal or greater than the
                //  version it was made obsolete in.
                if ((propInfo.ObsoleteVersion > 0) && (this.MinVersion >= propInfo.ObsoleteVersion))
                {
                    continue;
                }

                args.il.BeginScope();
                propInfo.GenerateWriteIL(args);
                args.il.EndScope();
            }

            if (this.IsInline == false)
            {
                //  Write saved unhandled data if current version is lower
                //  than the data we original deserialized from
                args.il.PushInt(this.CurrentVersion);
                args.il.CallMethod(args.headerVar, typeof(TypeSerializationHeader).GetProperty("DataVersion").GetGetMethod());
                args.il.GotoIfGreaterOrEqual(SkipUnhandledDataLabel);

                args.il.BeginCallMethod(args.streamVar, "Write", new Type[] { typeof(byte[]) });
                args.il.CallMethod(SerializationInfoVar, typeof(SerializationInfo).GetProperty("UnhandledData").GetGetMethod());
                args.il.CallMethod();

                args.il.MarkLabel(SkipUnhandledDataLabel);

                //  Update header with actual data length
                args.il.BeginCallMethod(args.headerVar, "UpdateDataLength", new Type[] { typeof(IPrimitiveWriter) });
                args.il.PushLocal(args.streamVar);
                args.il.CallMethod();

                //  if (createdNameTable)
                //  {
                //      _typeNameTable.Serialize();     -- Will do nothing if the table is empty
                //  }
                args.il.PushLocal(CreatedNameTableVar);
                args.il.GotoIfFalse(SkipNameTableSerializeLabel);

                args.il.BeginCallMethod(args.nameTableVar, "Serialize", new Type[] { typeof(IPrimitiveWriter) });
                args.il.PushLocal(args.streamVar);
                args.il.CallMethod();

                args.il.MarkLabel(SkipNameTableSerializeLabel);
            }

            args.il.Return();

            this.autoSerializeMethod = (AutoSerializeMethod)args.il.Compile(typeof(AutoSerializeMethod));
            this.serializeMethod     = SerializeAutoSerializable;
        }