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

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

            var travellerIndex = 0;
            foreach (var property in target.Properties) {
                var argField = _classBuilder.DefinePrivateField("_arg" + property.Ref.Name, members.VisitArgsType);
                var visitArgsCode = new CallMethodILCode(factoryArgument, members.ConstructVisitArgsMethod, property.Ref.Name);
                _constructorBuilder.IL.SetField(argField, visitArgsCode);
                argFields.Add(property, argField);

                Type[] types;
                if (!ReflectionAnalyzer.TryGetComplexTypes(property.Ext, out 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 = new CallMethodILCode(factoryArgument, members.ConstructVisitArgsWithTypeMethod, type);
                    var callConstructorCode = new CallConstructorILCode(dynamicTraveller.Constructor, getFactoryCode);
                    _constructorBuilder.IL.SetField(fieldBuilder, callConstructorCode);

                    //_constructorBuilder.IL.SetField(fieldBuilder, dynamicTraveller.Constructor);
                }
            }
            _constructorBuilder.IL.Return();

            var context = new TravellerContext(childTravellers, argFields);
            BuildWriteMethods(target, context);
            BuildReadMethods(target, context);

            _classBuilder.Seal();
            _dynamicTraveller.Complete(_classBuilder.Type);
        }
        private ILCodeParameter GenerateCollectionContent(ExtendedType target, string refName)
        {
            var collectionMembers = new CollectionMembers(target);
            var isValueType = collectionMembers.ElementType.IsValueType;

            var collectionLocal = _il.DeclareLocal("collection", collectionMembers.VariableType);
            _il.Construct(collectionMembers.Constructor);
            _il.Cast(collectionMembers.VariableType);
            _il.Var.Set(collectionLocal);

            var valueLocal = DeclareCollectionItemLocal("cv", collectionMembers.ElementType); ;

            if (collectionMembers.ElementTypeExt.IsValueOrNullableOfValue()) {
                _il.Snippets.WhileLoop(il => { // While condition
                    _il.Snippets.InvokeMethod(_visitorVariable, Members.VisitorTryVisitValue[collectionMembers.ElementType], Members.VisitArgsCollectionItem, valueLocal);

                    var valueNotFoundLabel = _il.DefineLabel();
                    _il.TransferLongIfFalse(valueNotFoundLabel);

                    if (isValueType) {
                        _il.Snippets.InvokeMethod(valueLocal, Members.Nullable[collectionMembers.ElementType].GetHasValue);
                    }
                    else {
                        _il.Snippets.AreEqual(valueLocal, ILCodeParameter.Null);
                        _il.Negate();
                    }

                    var isNullLabel = _il.DefineLabel();
                    _il.TransferLong(isNullLabel);

                    _il.MarkLabel(valueNotFoundLabel);
                    _il.LoadValue(0);
                    _il.MarkLabel(isNullLabel);
                }, il => {
                    _il.Var.Load(collectionLocal);

                    GenerateLoadParamValueCode(valueLocal);

                    _il.CallVirt(collectionMembers.Add);
                });
            }
            else if (collectionMembers.ElementTypeExt.Class == TypeClass.Dictionary) {
                _il.Snippets.WhileLoop(il => { // Condition
                    var callTryVisit = new CallMethodILCode(_visitorVariable, Members.VisitorTryVisit, Members.VisitArgsDictionaryInCollection);
                    _il.Snippets.AreEqual(callTryVisit, (int)ValueState.Found);
                }, il => {
                    var contentParam = GenerateDictionaryEnumerateCode(collectionMembers.ElementType, refName);
                    _il.Snippets.InvokeMethod(_visitorVariable, Members.VisitorLeave, Members.VisitArgsDictionaryInCollection);
                    _il.Snippets.InvokeMethod(collectionLocal, collectionMembers.Add, contentParam);
                });
            }
            else if (collectionMembers.ElementTypeExt.Class == TypeClass.Collection) {
                _il.Snippets.WhileLoop(il => { // Condition
                    var callTryVisit = new CallMethodILCode(_visitorVariable, Members.VisitorTryVisit, Members.VisitArgsCollectionInCollection);
                    _il.Snippets.AreEqual(callTryVisit, (int)ValueState.Found);
                }, il => {
                    var contentParam = GenerateCollectionContent(collectionMembers.ElementTypeExt, refName);
                    _il.Snippets.InvokeMethod(_visitorVariable, Members.VisitorLeave, Members.VisitArgsCollectionInCollection);
                    _il.Snippets.InvokeMethod(collectionLocal, collectionMembers.Add, contentParam);
                });
            }
            else {
                _il.Snippets.WhileLoop(il => {
                    var callTryVisit = new CallMethodILCode(_visitorVariable, Members.VisitorTryVisit, Members.VisitArgsCollectionItem);
                    _il.Snippets.AreEqual(callTryVisit, (int)ValueState.Found);
                }, il => {
                    GenerateCreateAndChildCallCode(valueLocal);
                    _il.Snippets.InvokeMethod(_visitorVariable, Members.VisitorLeave, Members.VisitArgsCollectionItem);
                    _il.Snippets.InvokeMethod(collectionLocal, collectionMembers.Add, valueLocal);
                });
            }
            if (target.Ref.IsArray)
                return new CallMethodILCode(collectionMembers.ToArray, collectionLocal);

            return collectionLocal;
        }
        private LocalILCodeVariable GenerateDictionaryEnumerateCode(Type type, string refName)
        {
            var extType = _il.TypeCache.Extend(type);
            var dictionaryMembers = new DictionaryMembers(extType);

            var local = _il.DeclareLocal("dictionary", dictionaryMembers.VariableType);
            _il.Construct(dictionaryMembers.Constructor);
            _il.Var.Set(local);

            NullableMembers keyNullableMembers;
            ILCodeParameter keyParam;

            var conditionLabel = _il.DefineLabel();
            var bodyLabel = _il.DefineLabel();
            var keyType = Members.Nullable.TryGetValue(dictionaryMembers.KeyType, out keyNullableMembers) ? keyNullableMembers.NullableType : dictionaryMembers.KeyType;
            var keyTypeExt = _il.TypeCache.Extend(keyType);

            // First of all, transfer to the condition part
            _il.TransferLong(conditionLabel);

            // Mark that we enter the body of the loop
            _il.MarkLabel(bodyLabel);
            if (keyTypeExt.IsValueOrNullableOfValue()) {
                keyParam = _il.DeclareLocal("ck", keyType);
            }
            else if (keyTypeExt.Class == TypeClass.Dictionary) {
                keyParam = GenerateDictionaryEnumerateCode(keyType, refName);

                _il.Snippets.InvokeMethod(_visitorVariable, Members.VisitorLeave, Members.VisitArgsDictionaryInDictionaryKey);
            }
            else if (keyTypeExt.Class == TypeClass.Collection) {
                keyParam = GenerateCollectionContent(keyTypeExt, refName);

                _il.Snippets.InvokeMethod(_visitorVariable, Members.VisitorLeave, Members.VisitArgsCollectionInDictionaryKey);
            }
            else {
                var keyLocal = _il.DeclareLocal("ck", keyType);
                GenerateCreateAndChildCallCode(keyLocal);
                keyParam = keyLocal;
                _il.Snippets.InvokeMethod(_visitorVariable, Members.VisitorLeave, Members.VisitArgsDictionaryKey);
            }

            var throwExceptionLabel = _il.DefineLabel();

            ILCodeParameter valueParam;

            NullableMembers valueNullableMembers;
            var hasNullableMembers = Members.Nullable.TryGetValue(dictionaryMembers.ValueType, out valueNullableMembers);
            var valueType = dictionaryMembers.ValueType;
            var extValueType = valueType.Extend();

            if (extValueType.IsValueOrNullableOfValue()) {
                var loadTrueLabel = _il.DefineLabel();
                var checkIfErrorLabel = _il.DefineLabel();

                valueParam = _il.DeclareLocal("cv", hasNullableMembers ? valueNullableMembers.NullableType : dictionaryMembers.ValueType);
                _il.Snippets.InvokeMethod(_visitorVariable, Members.VisitorTryVisitValue[dictionaryMembers.ValueType], Members.VisitArgsDictionaryValue, valueParam);
                _il.TransferLongIfFalse(loadTrueLabel);

                if (hasNullableMembers) {
                    _il.Snippets.InvokeMethod(valueParam, valueNullableMembers.GetHasValue);
                    _il.Negate();
                }
                else {
                    _il.Snippets.AreEqual(valueParam, null);
                }
                _il.TransferLong(checkIfErrorLabel);
                _il.MarkLabel(loadTrueLabel);
                _il.LoadValue(true);
                _il.MarkLabel(checkIfErrorLabel);
                _il.TransferLongIfTrue(throwExceptionLabel);
            }
            else if (extValueType.Class == TypeClass.Dictionary) {
                var callTryVisitValue = new CallMethodILCode(_visitorVariable, Members.VisitorTryVisit, Members.VisitArgsDictionaryInDictionaryValue);
                _il.Snippets.AreEqual(callTryVisitValue, (int)ValueState.Found);
                _il.TransferLongIfFalse(throwExceptionLabel);

                valueParam = GenerateDictionaryEnumerateCode(valueType, refName);

                _il.Snippets.InvokeMethod(_visitorVariable, Members.VisitorLeave, Members.VisitArgsDictionaryInDictionaryValue);
            }
            else if (extValueType.Class == TypeClass.Collection) {
                var callTryVisit = new CallMethodILCode(_visitorVariable, Members.VisitorTryVisit, Members.VisitArgsCollectionInDictionaryValue);
                _il.Snippets.AreEqual(callTryVisit, (int)ValueState.Found);
                _il.TransferLongIfFalse(throwExceptionLabel);

                valueParam = GenerateCollectionContent(_il.TypeCache.Extend(valueType), refName);

                _il.Snippets.InvokeMethod(_visitorVariable, Members.VisitorLeave, Members.VisitArgsCollectionInDictionaryValue);
            }
            else {
                var callTryVisit = new CallMethodILCode(_visitorVariable, Members.VisitorTryVisit, Members.VisitArgsDictionaryValue);
                _il.Snippets.AreEqual(callTryVisit, (int)ValueState.Found);
                _il.TransferLongIfFalse(throwExceptionLabel);

                var valueLocal = _il.DeclareLocal("cv", hasNullableMembers ? valueNullableMembers.NullableType : dictionaryMembers.ValueType);
                GenerateCreateAndChildCallCode(valueLocal);
                valueParam = valueLocal;

                _il.Snippets.InvokeMethod(_visitorVariable, Members.VisitorLeave, Members.VisitArgsDictionaryValue);
            }

            _il.Var.Load(local);
            GenerateLoadParamValueCode(keyParam);
            GenerateLoadParamValueCode(valueParam);

            _il.CallVirt(dictionaryMembers.Add);
            _il.TransferLong(conditionLabel);

            _il.MarkLabel(throwExceptionLabel);
            _il.Snippets.Throw(new CallMethodILCode(Members.ExceptionNoDictionaryValue, refName));

            _il.MarkLabel(conditionLabel);

            if (keyTypeExt.IsValueOrNullableOfValue()) {
                var loadZeroLabel = _il.DefineLabel();
                var checkConditionLabel = _il.DefineLabel();

                _il.Snippets.InvokeMethod(_visitorVariable, Members.VisitorTryVisitValue[keyType], Members.VisitArgsDictionaryKey, keyParam);
                _il.TransferLongIfFalse(loadZeroLabel);

                if (keyType.IsValueType) {
                    _il.Snippets.InvokeMethod(keyParam, Members.Nullable[keyType].GetHasValue);
                    _il.TransferLong(checkConditionLabel);
                }
                else {
                    _il.Snippets.AreEqual(keyParam, ILCodeParameter.Null);
                    _il.Negate();
                    _il.TransferLong(checkConditionLabel);
                }

                _il.MarkLabel(loadZeroLabel);
                _il.LoadValue(false);
                _il.MarkLabel(checkConditionLabel);
                _il.TransferLongIfTrue(bodyLabel);

            }
            else if (keyTypeExt.Class == TypeClass.Dictionary) {
                var callTryVisitKey = new CallMethodILCode(_visitorVariable, Members.VisitorTryVisit, Members.VisitArgsDictionaryInDictionaryKey);
                _il.Snippets.AreEqual(callTryVisitKey, (int)ValueState.Found);
                _il.TransferLongIfTrue(bodyLabel);
            }
            else if (keyTypeExt.Class == TypeClass.Collection) {
                var callTryVisitKey = new CallMethodILCode(_visitorVariable, Members.VisitorTryVisit, Members.VisitArgsCollectionInDictionaryKey);
                _il.Snippets.AreEqual(callTryVisitKey, (int)ValueState.Found);
                _il.TransferLongIfTrue(bodyLabel);
            }
            else {
                var callTryVisit = new CallMethodILCode(_visitorVariable, Members.VisitorTryVisit, Members.VisitArgsDictionaryKey);
                _il.Snippets.AreEqual(callTryVisit, (int)ValueState.Found);
                _il.TransferLongIfTrue(bodyLabel);
            }

            return local;
        }
Exemple #4
0
 public static ILCodeParameter Of(CallMethodILCode code)
 {
     return new ILCodeParameterDelegatable(code.ReturnType, il => il.Generate(code));
 }