public void BuildTraveller()
        {
            if (_classBuilder.IsSealed)
            {
                throw new InvalidOperationException("Classification builder is sealed");
            }
            var target          = _typeProvider.GetOrCreate(_type);
            var members         = _dtContext.Members;
            var factoryArgument = new ILArgPointer(members.VisitArgsFactoryType, 1);


            var childTravellers = new Dictionary <Type, ChildTravellerInfo>();
            var argFields       = new Dictionary <SerializableProperty, FieldInfo>();

            var il             = _constructorBuilder.IL;
            var travellerIndex = 0;

            foreach (var property in target.Properties)
            {
                var argField      = _classBuilder.DefinePrivateField("_arg" + property.Ref.Name, members.VisitArgsType);
                var visitArgsCode = new ILCallMethodSnippet(factoryArgument, members.ConstructVisitArgsMethod, property.Ref.Name);
                il.Set(ILPointer.This(), argField, visitArgsCode);
                argFields.Add(property, argField);

                if (!ReflectionAnalyzer.TryGetComplexTypes(property.Ext, out var types))
                {
                    continue;
                }

                foreach (var type in types)
                {
                    if (childTravellers.ContainsKey(type))
                    {
                        continue;
                    }

                    var dynamicTraveller = _dtContext.Get(type);
                    var interfaceType    = typeof(IGraphTraveller <>).MakeGenericType(type);
                    var fieldBuilder     = _classBuilder.DefinePrivateField(string.Concat("_traveller", type.Name, ++travellerIndex), interfaceType);
                    childTravellers.Add(type, new ChildTravellerInfo {
                        Field             = fieldBuilder,
                        TravelWriteMethod = dynamicTraveller.TravelWriteMethod,
                        TravelReadMethod  = dynamicTraveller.TravelReadMethod
                    });

                    var getFactoryCode = ILSnippet.Call(factoryArgument, members.ConstructVisitArgsWithTypeMethod, type);
                    var newTraveller   = ILPointer.New(dynamicTraveller.Constructor, getFactoryCode);
                    il.Set(ILPointer.This(), fieldBuilder, newTraveller);
                }
            }
            il.Emit(OpCodes.Ret);

            var context = new TravellerContext(childTravellers, argFields);

            BuildWriteMethods(target, context);
            BuildReadMethods(target, context);

            _classBuilder.Seal();
            DynamicTraveller.Complete(_classBuilder.Type);
        }
        private void BuildReadMethods(SerializableType target, TravellerContext context)
        {
            var typedMethodBuilder = _travelReadMethod;
            var readBuilder        = new DynamicReadTravellerBuilder(typedMethodBuilder, target, context, _typeProvider.Provider);

            readBuilder.BuildTravelReadMethod();

            var untypedMethodBuilder = _classBuilder.DefineOverloadMethod("Travel", typeof(void), new[] { typeof(IReadVisitor), typeof(object) });
            var il = untypedMethodBuilder.IL;

            il.InvokeMethod(ILPointer.This(),
                            typedMethodBuilder.Method,
                            ILPointer.Arg(1, typeof(IReadVisitor)),
                            ILPointer.Arg(2, typeof(object)).Cast(target.Type));

            il.Emit(OpCodes.Ret);
        }
        public DynamicTravellerBuilder(DynamicTravellerContext dtContext, ClassBuilder classBuilder, SerializableTypeProvider typeProvider, Type type)
        {
            _dtContext          = dtContext;
            _classBuilder       = classBuilder;
            _typeProvider       = typeProvider;
            _type               = type;
            _constructorBuilder = _classBuilder.DefineConstructor(typeof(IVisitArgsFactory));

            var baseConstructor = typeof(object).GetTypeInfo().GetConstructor(Type.EmptyTypes);
            var il = _constructorBuilder.IL;

            il.Load(ILPointer.This());
            il.Emit(OpCodes.Call, baseConstructor);

            _travelWriteMethod = _classBuilder.DefineOverloadMethod("Travel", typeof(void), new[] { typeof(IWriteVisitor), _type });
            _travelReadMethod  = _classBuilder.DefineOverloadMethod("Travel", typeof(void), new[] { typeof(IReadVisitor), _type });

            DynamicTraveller = new DynamicTraveller(_classBuilder.Type, _constructorBuilder.Reference, _travelWriteMethod.Method, _travelReadMethod.Method, dtContext.Members);
        }
        private void GenerateCreateAndChildCallCode(ILLocalVariable local)
        {
            var type = local.Type;

            var constructor = type.GetTypeInfo().GetConstructor(Type.EmptyTypes);

            if (constructor == null)
            {
                throw InvalidGraphException.NoParameterLessConstructor(type);
            }

            _il.Construct(constructor);
            _il.Set(local);

            var childTravellerInfo = _context.GetTraveller(type);

            var field = ILPointer.Field(ILPointer.This(), childTravellerInfo.Field);

            _il.InvokeMethod(field, childTravellerInfo.TravelReadMethod, _visitorVariable, local);
        }
        private void GeneratePropertyCode(ILArgPointer graphArg, SerializableProperty target)
        {
            var extPropertyType   = target.Ext;
            var argsField         = _context.GetArgsField(target);
            var argsFieldVariable = ILPointer.Field(ILPointer.This(), argsField);

            if (extPropertyType.IsValueOrNullableOfValue())
            {
                var isNullable  = extPropertyType.Classification == TypeClassification.Nullable;
                var isEnum      = extPropertyType.IsEnum();
                var isValueType = extPropertyType.Info.IsValueType;

                var mediatorPropertyType = isEnum ? extPropertyType.GetUnderlyingEnumType() : extPropertyType.Ref;
                var valueType            = !isNullable && isValueType ? Members.Nullable[mediatorPropertyType].NullableType : mediatorPropertyType;

                var valueLocal = _il.NewLocal(valueType);

                _il.InvokeMethod(_visitorVariable, Members.VisitorTryVisitValue[valueType], argsFieldVariable, valueLocal);

                if (isValueType && !isNullable)
                {
                    var labelValueNotFound = _il.NewLabel();
                    labelValueNotFound.TransferLongIfFalse();

                    _il.Load(valueLocal
                             .Call(Members.Nullable[mediatorPropertyType].GetHasValue)
                             .Negate());

                    var nullableHasValueLabel = _il.NewLabel();
                    nullableHasValueLabel.TransferLong();

                    labelValueNotFound.Mark();
                    _il.Load(true);
                    nullableHasValueLabel.Mark();
                }
                else
                {
                    _il.Negate();
                }

                var skipSetValueLabel = _il.NewLabel();
                skipSetValueLabel.TransferLongIfTrue();

                var valueToAdd = isValueType && !isNullable
                    ? valueLocal.Call(Members.Nullable[mediatorPropertyType].GetValue)
                    : valueLocal;

                _il.Set(graphArg, target.Ref, valueToAdd);

                skipSetValueLabel.Mark();
            }
            else if (extPropertyType.Classification == TypeClassification.Dictionary)
            {
                var stateLocal = _il.NewLocal(typeof(ValueState));
                _il.Set(stateLocal, _visitorVariable.Call(Members.VisitorTryVisit, argsFieldVariable));

                var endLabel = _il.NewLabel();
                endLabel.TransferLongIfTrue(stateLocal.Equal((int)ValueState.NotFound));

                _il.AreEqual(stateLocal, (int)ValueState.Found);

                var nullLabel = _il.NewLabel();
                nullLabel.TransferLongIfFalse();

                var dictionaryLocal = GenerateDictionaryEnumerateCode(target.Ref.PropertyType, target.Ref.Name);

                _il.Set(graphArg, target.Ref, dictionaryLocal.Cast(target.Ref.PropertyType));

                _il.InvokeMethod(_visitorVariable, Members.VisitorLeave, argsFieldVariable);

                endLabel.TransferLong();

                nullLabel.Mark();
                _il.Set(graphArg, target.Ref, null);

                endLabel.Mark();
            }
            else if (extPropertyType.Classification == TypeClassification.Collection)
            {
                _il.InvokeMethod(_visitorVariable, Members.VisitorTryVisit, argsFieldVariable);
                var stateLocal = _il.NewLocal(typeof(ValueState));
                _il.Set(stateLocal);

                _il.AreEqual(stateLocal, (int)ValueState.NotFound);

                var endLabel = _il.NewLabel();
                endLabel.TransferLongIfTrue();

                _il.AreEqual(stateLocal, (int)ValueState.Found);

                var nullLabel = _il.NewLabel();
                nullLabel.TransferLongIfFalse();

                var collectionParam = GenerateCollectionContent(extPropertyType, target.Ref.Name);

                _il.Set(graphArg, target.Ref, collectionParam);
                _il.InvokeMethod(_visitorVariable, Members.VisitorLeave, argsFieldVariable);
                endLabel.TransferLong();

                nullLabel.Mark();
                _il.Set(graphArg, target.Ref, null);

                endLabel.Mark();
            }
            else
            {
                _il.InvokeMethod(_visitorVariable, Members.VisitorTryVisit, argsFieldVariable);
                var stateLocal = _il.NewLocal(typeof(ValueState));
                _il.Set(stateLocal);

                _il.IfNotEqual(stateLocal, (int)ValueState.NotFound)
                .Then(() => {
                    _il.IfEqual(stateLocal, (int)ValueState.Found)
                    .Then(() => {
                        var singleLocal = _il.NewLocal(extPropertyType.Ref);
                        GenerateCreateAndChildCallCode(singleLocal);

                        _il.Set(graphArg, target.Ref, singleLocal);
                        _il.InvokeMethod(_visitorVariable, Members.VisitorLeave, argsFieldVariable);
                    }).Else(() => {
                        _il.Set(graphArg, target.Ref, null);
                    }).End();
                }).End();
            }
        }
        private void GeneratePropertyCode(ILPointer graphPointer, SerializableProperty target)
        {
            var extPropertyType   = target.Ext;
            var argsField         = _context.GetArgsField(target);
            var argsFieldVariable = ILPointer.Field(ILPointer.This(), argsField);

            if (target.Ext.IsValueOrNullableOfValue())
            {
                var valueType = target.Ext.IsEnum()
                    ? target.Ext.GetUnderlyingEnumType()
                    : target.Ref.PropertyType;

                var propertyParameter = ILPointer.Property(graphPointer, target.Ref).AsNullable();

                _il.InvokeMethod(_visitorVariable, Members.VisitorVisitValue[valueType], propertyParameter, argsFieldVariable);
            }
            else if (extPropertyType.Classification == TypeClassification.Dictionary)
            {
                var container = extPropertyType.Container.AsDictionary();

                var dictionaryType = container.DictionaryInterfaceType;
                var cLocal         = _il.NewLocal(dictionaryType);
                _il.Set(cLocal, ILPointer.Property(graphPointer, target.Ref).Cast(dictionaryType));

                _il.InvokeMethod(_visitorVariable, Members.VisitorVisit, cLocal, argsFieldVariable);

                _il.IfNotEqual(cLocal, null)
                .Then(() => GenerateDictionaryCode(cLocal, container.ElementType))
                .End();

                _il.InvokeMethod(_visitorVariable, Members.VisitorLeave, cLocal, argsFieldVariable);
            }
            else if (extPropertyType.Classification == TypeClassification.Collection)
            {
                var container = extPropertyType.Container.AsCollection();

                var collectionType = extPropertyType.Ref.IsArray && extPropertyType.Container.AsArray().Ranks > 1
                    ? extPropertyType.Ref
                    : container.CollectionInterfaceType;

                var cLocal = _il.NewLocal(collectionType);

                _il.Set(cLocal, ILPointer.Property(graphPointer, target.Ref).Cast(collectionType));

                _il.InvokeMethod(_visitorVariable, Members.VisitorVisit, cLocal, argsFieldVariable);

                _il.IfNotEqual(cLocal, null)
                .Then(() => GenerateEnumerateCollectionContentCode(extPropertyType, cLocal))
                .End();

                _il.InvokeMethod(_visitorVariable, Members.VisitorLeave, cLocal, argsFieldVariable);
            }
            else
            {
                var singleLocal = _il.NewLocal(target.Ref.PropertyType);
                _il.Set(singleLocal, ILPointer.Property(graphPointer, target.Ref));

                _il.InvokeMethod(_visitorVariable, Members.VisitorVisit, singleLocal, argsFieldVariable);

                var checkIfNullLabel = _il.NewLabel();
                checkIfNullLabel.TransferIfNull(singleLocal);

                GenerateChildCall(singleLocal);

                checkIfNullLabel.Mark();

                _il.InvokeMethod(_visitorVariable, Members.VisitorLeave, singleLocal, argsFieldVariable);
            }
        }