private static Expression CreateOrResizeArrayIfNeeded(ModelConfigurationNode child, Expression path, ParameterExpression arrayParameter)
        {
            Expression destArrayIsNull = Expression.ReferenceEqual(path, Expression.Constant(null, path.Type));
            Expression resizeIfNeeded;

            if (path.Type.IsArray)
            {
                Expression lengthsAreDifferent = Expression.OrElse(destArrayIsNull, Expression.NotEqual(Expression.ArrayLength(path), Expression.ArrayLength(arrayParameter)));
                var        temp = Expression.Parameter(path.Type, path.Type.Name);
                resizeIfNeeded = Expression.IfThen(
                    lengthsAreDifferent,
                    Expression.IfThenElse(destArrayIsNull,
                                          path.Assign(child.ConfiguratorType, Expression.NewArrayBounds(child.NodeType, Expression.ArrayLength(arrayParameter))),
                                          Expression.Block(new[] { temp }, Expression.Assign(temp, path), Expression.Call(arrayResizeMethod.MakeGenericMethod(child.NodeType), temp, Expression.ArrayLength(arrayParameter)), path.Assign(child.ConfiguratorType, temp))
                                          ));
            }
            else if (path.Type.IsGenericType && path.Type.GetGenericTypeDefinition() == typeof(List <>))
            {
                Expression lengthsAreDifferent = Expression.NotEqual(Expression.Property(path, "Count"), Expression.ArrayLength(arrayParameter));
                var        expressions         = new List <Expression>();
                if (path.NodeType == ExpressionType.MemberAccess && CanWrite(((MemberExpression)path).Member))
                {
                    expressions.Add(Expression.IfThen(destArrayIsNull, Expression.Assign(path, Expression.New(path.Type.GetConstructor(new[] { typeof(int) }), Expression.ArrayLength(arrayParameter)))));
                }
                expressions.Add(Expression.Call(listResizeMethod.MakeGenericMethod(child.NodeType), path, Expression.ArrayLength(arrayParameter)));
                resizeIfNeeded = Expression.IfThen(lengthsAreDifferent, Expression.Block(expressions));
            }
            else
            {
                throw new NotSupportedException("Enumeration over '" + path.Type + "' is not supported");
            }

            return(resizeIfNeeded);
        }
 private ModelConfigurationTreeTraveler(bool createPath, [CanBeNull] ModelConfigurationNode subRoot)
 {
     this.createPath  = createPath;
     this.subRoot     = subRoot;
     SubRootIsVisited = false;
     ArrayAliases     = new List <KeyValuePair <Expression, Expression> >();
 }
        public static string ToPrettyString(this ModelConfigurationNode node)
        {
            var allMutators = new List <MutatorWithPath>();

            node.GetMutatorsWithPath(allMutators);
            return(ToPrettyString(allMutators));
        }
Ejemplo n.º 4
0
        private static void Qxx(ModelConfigurationNode destTree, List <KeyValuePair <Expression, Expression> > conditionalSetters, MutatorConfiguration mutatedMutator, CompositionPerformer performer, Expression resolvedKey)
        {
            var        unconditionalSetter = conditionalSetters.SingleOrDefault(pair => pair.Value == null);
            Expression invertedCondition   = null;

            foreach (var setter in conditionalSetters)
            {
                var mutatedPath = setter.Key;
                var condition   = setter.Value;
                if (condition == null)
                {
                    continue;
                }
                Expression currentInvertedCondition = Expression.Not(condition);
                invertedCondition = invertedCondition == null ? currentInvertedCondition : Expression.AndAlso(invertedCondition, currentInvertedCondition);
                var primaryDependencies = Expression.Lambda(mutatedPath, mutatedPath.ExtractParameters()).ExtractPrimaryDependencies().Select(lambda => lambda.Body);
                var commonPath          = primaryDependencies.FindLCP();
                var destNode            = commonPath == null ? destTree : destTree.Traverse(commonPath, true);
                destNode.mutators.Add(PurgeFilters(mutatedPath, mutatedMutator.If(Expression.Lambda(condition, condition.ExtractParameters()))));
            }

            {
                var mutatedPath         = unconditionalSetter.Key ?? performer.Perform(resolvedKey);
                var primaryDependencies = Expression.Lambda(mutatedPath, mutatedPath.ExtractParameters()).ExtractPrimaryDependencies().Select(lambda => lambda.Body);
                var commonPath          = primaryDependencies.FindLCP();
                var destNode            = commonPath == null ? destTree : destTree.Traverse(commonPath, true);
                destNode.mutators.Add(PurgeFilters(mutatedPath, invertedCondition == null ? mutatedMutator : mutatedMutator.If(Expression.Lambda(invertedCondition, invertedCondition.ExtractParameters()))));
            }
        }
        private ModelConfigurationNode GotoArrayIndexOrEachElement([CanBeNull] ModelConfigurationNode child, [NotNull] Expression index)
        {
            if (child == null)
            {
                return(null);
            }
            var newChild = child.GotoArrayElement(GetIndex(index), createPath);

            if (newChild != null)
            {
                return(newChild);
            }

            // If no child exists and create:false create array alias for migration and go by 'Each' edge
            var array = GetArrayMigrationAlias(child);

            if (array != null)
            {
                ArrayAliases.Add(new KeyValuePair <Expression, Expression>(
                                     Expression.ArrayIndex(array, index),
                                     array.MakeEachCall()));
            }

            return(child.GotoEachArrayElement(create: false));
        }
Ejemplo n.º 6
0
        internal static bool Traverse([NotNull] this ModelConfigurationNode node, [NotNull] Expression path, [CanBeNull] ModelConfigurationNode subRoot, [CanBeNull] out ModelConfigurationNode child, bool create)
        {
            var traverseResult = ModelConfigurationTreeTraveler.Traverse(node, path, subRoot, create);

            child = traverseResult.Child;
            return(traverseResult.SubRootIsVisited);
        }
        public static LambdaExpression BuildTreeMutator(this ModelConfigurationNode node, Type sourceType)
        {
            var destParameter   = node.Parent == null ? (ParameterExpression)node.Path : Expression.Parameter(node.NodeType, node.NodeType.Name);
            var sourceParameter = Expression.Parameter(sourceType, sourceType.Name);

            return(node.BuildTreeMutator(destParameter, sourceParameter));
        }
Ejemplo n.º 8
0
        private static Expression GetArrayMigrationAlias(ModelConfigurationNode node)
        {
            var arrays    = node.GetArrays();
            var childPath = new ExpressionWrapper(node.Path, strictly: false);

            return(arrays.Values.FirstOrDefault(arrayPath => !childPath.Equals(new ExpressionWrapper(arrayPath, strictly: false))));
        }
        /// <summary>
        ///     Просто выполняет переход по ребру и строит выражения для перечисления массивов и словарей.
        /// </summary>
        private static void BuildTreeMutator(this ModelConfigurationNode node, ModelConfigurationEdge edge, Stack <ModelConfigurationEdge> edges, ModelConfigurationNode root, List <KeyValuePair <Expression, Expression> > aliases, List <Expression> localResult,
                                             HashSet <ModelConfigurationNode> visitedNodes, HashSet <ModelConfigurationNode> processedNodes, List <Expression> globalResult, List <ParameterExpression> invariantParameters)
        {
            var child = node.children[edge];

            if (edge.IsMemberAccess || edge.IsArrayIndex || edge.IsConvertation || edge.IsIndexerParams)
            {
                child.BuildTreeMutator(edges, root, aliases, localResult, visitedNodes, processedNodes, globalResult, invariantParameters);
            }
            else if (edge.IsEachMethod)
            {
                var path = node.Path.ResolveAliases(aliases);
                if (node.NodeType.IsDictionary())
                {
                    node.BuildTreeMutatorForDictionary(child, edges, root, aliases, localResult, visitedNodes, processedNodes, path, globalResult, invariantParameters);
                }
                else
                {
                    node.BuildTreeMutatorForArray(child, edges, root, aliases, localResult, visitedNodes, processedNodes, path, globalResult, invariantParameters);
                }
            }
            else
            {
                throw new InvalidOperationException();
            }
        }
        private ModelConfigurationNode GotoIndexerOrEachElement([CanBeNull] ModelConfigurationNode child, [NotNull] Expression[] indexerArguments)
        {
            if (child == null)
            {
                return(null);
            }

            var indexerParameters = indexerArguments.Select(exp => ((ConstantExpression)exp).Value).ToArray();
            // Try go by 'indexer' edge
            var newChild = child.GotoIndexer(indexerParameters, createPath);

            if (newChild != null)
            {
                return(newChild);
            }

            // If no child exists and create:false go by 'each' edge and create array alias for migration
            newChild = child.GotoEachArrayElement(create: false);
            if (newChild == null)
            {
                return(null);
            }

            var array = GetArrayMigrationAlias(child);

            if (array != null)
            {
                ArrayAliases.Add(new KeyValuePair <Expression, Expression>(
                                     Expression.Call(array, array.Type.GetProperty("Item", BindingFlags.Public | BindingFlags.Instance).GetGetMethod(), indexerArguments),
                                     Expression.Property(array.MakeEachCall(), "Value")));
            }

            return(newChild.GotoMember(newChild.NodeType.GetProperty("Value", BindingFlags.Public | BindingFlags.Instance), create: false));
        }
Ejemplo n.º 11
0
 private static ModelConfigurationNode GoTo(ModelConfigurationNode node, ModelConfigurationEdge edge)
 {
     if (node == null)
     {
         return(null);
     }
     return(node.children.TryGetValue(edge, out var result) ? result : null);
 }
        private ModelConfigurationNode Traverse([NotNull] ModelConfigurationNode node, [NotNull] Expression path)
        {
            var child = TraverseInternal(node, path);

            SubRootIsVisited |= child == subRoot;

            return(child);
        }
 public static void GetMutatorsWithPath(this ModelConfigurationNode node, List <MutatorWithPath> result)
 {
     result.AddRange(node.GetMutatorsWithPath());
     foreach (var child in node.Children)
     {
         GetMutatorsWithPath(child, result);
     }
 }
Ejemplo n.º 14
0
        public static ModelConfigurationNode Traverse(this ModelConfigurationNode node, Expression path, bool create, out List <KeyValuePair <Expression, Expression> > arrayAliases)
        {
            ModelConfigurationNode result;

            arrayAliases = new List <KeyValuePair <Expression, Expression> >();
            node.Traverse(path, null, out result, create, arrayAliases);
            return(result);
        }
Ejemplo n.º 15
0
        public static void AddMutatorSmart(this ModelConfigurationNode node, LambdaExpression path, MutatorConfiguration mutator)
        {
            path = (LambdaExpression)path.Simplify();
            LambdaExpression filter;
            var simplifiedPath = PathSimplifier.SimplifyPath(path, out filter);

            mutator = mutator.ResolveAliases(ExpressionAliaser.CreateAliasesResolver(simplifiedPath.Body, path.Body));
            node.Traverse(simplifiedPath.Body, true).AddMutator(path.Body, filter == null ? mutator : mutator.If(filter));
        }
Ejemplo n.º 16
0
        internal CompositionPerformer(Type from, Type to, ModelConfigurationNode convertationTree)
        {
            From = from;
            To   = to;
            this.convertationTree = convertationTree;
            var parameter = Expression.Parameter(to);

            lambda = Expression.Lambda(parameter, parameter);
        }
Ejemplo n.º 17
0
 internal static MutatorWithPath[] GetMutatorsWithPath(this ModelConfigurationNode node)
 {
     return(node.Mutators.Select(mutator => new MutatorWithPath
     {
         PathToNode = node.Path,
         PathToMutator = mutator.Key,
         Mutator = mutator.Value
     }).ToArray());
 }
        public static Expression GetAlienArray(this ModelConfigurationNode node)
        {
            var arrays = node.GetArrays();

            if (!arrays.TryGetValue(node.RootType, out var result))
            {
                return(null);
            }
            return(ExpressionEquivalenceChecker.Equivalent(Expression.Lambda(result, result.ExtractParameters()).ExtractPrimaryDependencies()[0].Body, node.Path, false, true) ? null : result);
        }
Ejemplo n.º 19
0
        private void AssertEquivalentTrees(ModelConfigurationNode expected, ModelConfigurationNode actual)
        {
            actual.NodeType.Should().Be(expected.NodeType);
            actual.RootType.Should().Be(expected.RootType);
            actual.Edge.Should().Be(expected.Edge);
            AssertEquivalentExpressions(expected.Path, actual.Path);

            actual.children.Should().BeEquivalentTo(expected.children, config => config.Using <ModelConfigurationNode>(x => AssertEquivalentTrees(x.Expectation, x.Subject))
                                                    .WhenTypeIs <ModelConfigurationNode>());
        }
Ejemplo n.º 20
0
        private void DoTest(NodeBuilder treeBuilder, params LambdaExpression[] pathsToTraverse)
        {
            var expectedTree = treeBuilder.Build();
            var root         = ModelConfigurationNode.CreateRoot(null, typeof(Root));

            foreach (var path in pathsToTraverse)
            {
                root.Traverse(path.Body, create: true);
            }
            AssertEquivalentTrees(expectedTree, root);
        }
Ejemplo n.º 21
0
 public static void FindSubNodes(this ModelConfigurationNode node, List <ModelConfigurationNode> result)
 {
     if (node.children.Count == 0 && node.Mutators.Count > 0)
     {
         result.Add(node);
     }
     foreach (var child in node.Children)
     {
         child.FindSubNodes(result);
     }
 }
        internal static LambdaExpression BuildTreeValidator(this ModelConfigurationNode node, IPathFormatter pathFormatter)
        {
            var allMutators = new Dictionary <ExpressionWrapper, List <MutatorConfiguration> >();

            node.GetMutators(allMutators);

            var root = new ZzzNode();

            foreach (var pair in allMutators)
            {
                var arrays    = GetArrays(node.RootType, pair.Key.Expression, pair.Value);
                var arrayNode = arrays.Aggregate(root, (current, array) => current.Traverse(array));
                arrayNode.mutators.Add(new KeyValuePair <Expression, List <MutatorConfiguration> >(pair.Key.Expression, pair.Value));
            }

            var parameter    = node.Parent == null ? (ParameterExpression)node.Path : Expression.Parameter(node.NodeType, node.NodeType.Name);
            var treeRootType = ValidationResultTreeNodeBuilder.BuildType(parameter.Type);
            var result       = Expression.Parameter(typeof(ValidationResultTreeNode), "tree");
            var priority     = Expression.Parameter(typeof(int), "priority");
            var aliases      = new List <KeyValuePair <Expression, Expression> > {
                new KeyValuePair <Expression, Expression>(parameter, node.Path)
            };

            root = GetArrays(node.RootType, node.Path, new MutatorConfiguration[0]).Aggregate(root, (current, array) => current.Traverse(array));

            var validationResults = new List <Expression>();

            root.BuildValidator(pathFormatter, node, aliases, new Dictionary <ParameterExpression, ExpressionPathsBuilder.SinglePaths>(), result, treeRootType, priority, validationResults);

            //validationResults = validationResults.Select(exp => ExtractLoopInvariantFatExpressions(exp, new []{parameter}, expression => expression)).ToList();
            validationResults = validationResults.SplitToBatches(parameter, result, priority);

            Expression body;

            switch (validationResults.Count)
            {
            case 0:
                body = Expression.Empty();
                break;

            case 1:
                body = validationResults[0];
                break;

            default:
                body = Expression.Block(validationResults);
                break;
            }

            body = body.ExtractLoopInvariantFatExpressions(new[] { parameter }, expression => expression);
            var lambda = Expression.Lambda(body, parameter, result, priority);

            return(lambda);
        }
        public static TraverseResult Traverse([NotNull] ModelConfigurationNode node, [NotNull] Expression path, [CanBeNull] ModelConfigurationNode subRoot, bool create)
        {
            var traveler = new ModelConfigurationTreeTraveler(create, subRoot);
            var child    = traveler.Traverse(node, path);

            return(new TraverseResult
            {
                Child = child,
                ArrayAliases = traveler.ArrayAliases,
                SubRootIsVisited = traveler.SubRootIsVisited,
            });
        }
 private static void CheckDependencies(ModelConfigurationNode root, MutatorConfiguration mutator)
 {
     if (root == null || root.Root == root || mutator == null || mutator.Dependencies == null)
     {
         return;
     }
     foreach (var dependency in mutator.Dependencies)
     {
         if (!root.Root.Traverse(dependency.Body, root, out _, false))
         {
             throw new FoundExternalDependencyException("Unable to build validator for the subtree '" + root.Parent + "' due to the external dependency '" + dependency + "'");
         }
     }
 }
Ejemplo n.º 25
0
        private static ModelConfigurationNode GotoIndexer(this ModelConfigurationNode node, object[] parameters, bool create)
        {
            var edge     = new ModelConfigurationEdge(parameters);
            var property = node.NodeType.GetProperty("Item", BindingFlags.Public | BindingFlags.Instance);

            if (property == null)
            {
                throw new InvalidOperationException("Type '" + node.NodeType + "' doesn't contain indexer");
            }
            if (!property.CanRead || !property.CanWrite)
            {
                throw new InvalidOperationException("Type '" + node.NodeType + "' has indexer that doesn't contain either getter or setter");
            }
            return(node.GetChild(edge, property.GetGetMethod().ReturnType, create));
        }
Ejemplo n.º 26
0
        internal static ModelConfigurationNode GotoMember(this ModelConfigurationNode node, MemberInfo member, bool create)
        {
            var edge = new ModelConfigurationEdge(member);

            switch (member.MemberType)
            {
            case MemberTypes.Field:
                return(node.GetChild(edge, ((FieldInfo)member).FieldType, create));

            case MemberTypes.Property:
                return(node.GetChild(edge, ((PropertyInfo)member).PropertyType, create));

            default:
                throw new NotSupportedException("Member rootType " + member.MemberType + " is not supported");
            }
        }
Ejemplo n.º 27
0
        public static void AddMutator(this ModelConfigurationNode node, Expression path, MutatorConfiguration mutator)
        {
            if (mutator.IsUncoditionalSetter())
            {
                for (var i = 0; i < node.mutators.Count; ++i)
                {
                    if (node.mutators[i].Value.IsUncoditionalSetter() && ExpressionEquivalenceChecker.Equivalent(path, node.mutators[i].Key, false, false))
                    {
                        node.mutators[i] = new KeyValuePair <Expression, MutatorConfiguration>(path, mutator);
                        return;
                    }
                }
            }

            node.mutators.Add(new KeyValuePair <Expression, MutatorConfiguration>(path, mutator));
        }
 /// <param name="edges">Рёбра, ведущие до зависимости. Используем их, чтобы не запускать повторно на этом пути <see cref="BuildNodeMutator">BuildNodeMutator</see>, который иначе сдохнет "найдя циклическую зависимость", и не обходить других детей. </param>
 /// <param name="root">Корень поддерева, для которого мы строим Expression.</param>
 /// <param name="aliases">Алиасы для массивов и их текущих индексов + алиас на корень.</param>
 /// <param name="localResult">Локальный список построенных Expression-ов. Используется, когда нам нужно получить все Expression-ы в текущем поддереве (например, при построении циклов по массивам).</param>
 /// <param name="visitedNodes">Список посещённых нод (для которых мы вызвали <see cref="BuildNodeMutator">BuildNodeMutator</see>). Используется для поиска циклов в зависимостях.</param>
 /// <param name="processedNodes">Список обработанных нод (для которых мы вызвали <see cref="BuildNodeMutator">BuildNodeMutator</see> и он уже закончился). Используется для поиска циклов в зависимостях.</param>
 /// <param name="globalResult">Общий список, куда складываем все построенные Expression-ы.</param>
 /// <param name="invariantParameters">Список параметров, использующихся в <see cref="LoopInvariantFatExpressionsExtractor.ExtractLoopInvariantFatExpressions">ExtractLoopInvariantFatExpressions</see> для оптимизации циклов. Эти параметры - синонимы путей от source</param>
 private static void BuildTreeMutator(this ModelConfigurationNode node, Stack <ModelConfigurationEdge> edges, ModelConfigurationNode root, List <KeyValuePair <Expression, Expression> > aliases, List <Expression> localResult,
                                      HashSet <ModelConfigurationNode> visitedNodes, HashSet <ModelConfigurationNode> processedNodes, List <Expression> globalResult, List <ParameterExpression> invariantParameters)
 {
     // Если у нас есть ещё рёбра - переходим по ним, 'телепортируясь' к ноде с нужной нам зависимостью.
     if (edges != null && edges.Count != 0)
     {
         node.BuildTreeMutator(edges.Pop(), edges, root, aliases, localResult, visitedNodes, processedNodes, globalResult, invariantParameters);
     }
     else
     {
         node.BuildNodeMutator(root, aliases, localResult, visitedNodes, processedNodes, globalResult, invariantParameters);
         foreach (var entry in node.children)
         {
             node.BuildTreeMutator(entry.Key, edges, root, aliases, localResult, visitedNodes, processedNodes, globalResult, invariantParameters);
         }
     }
 }
Ejemplo n.º 29
0
        public static Dictionary <Type, Expression> GetArrays(this ModelConfigurationNode node)
        {
            var arrays = new Dictionary <Type, List <Expression> >();

            node.GetArrays(node.Path, arrays);
            return(arrays
                   .Where(pair => pair.Value.Count > 0)
                   .ToDictionary(pair => pair.Key,
                                 pair => pair.Value
                                 .GroupBy(expression => new ExpressionWrapper(expression, strictly: false))
                                 .Select(grouping =>
            {
                var exp = grouping.First();
                return exp.NodeType == ExpressionType.Call ? ((MethodCallExpression)exp).Arguments[0] : exp;
            })
                                 .FirstOrDefault()));
        }
        /// <summary>
        ///     Строит жирный Expression, содержаший все мутации или валидации.
        ///     Все, что нужно после этого - скомпилировать.
        /// </summary>
        private static LambdaExpression BuildTreeMutator(this ModelConfigurationNode node, [NotNull] ParameterExpression destParameter, [CanBeNull] ParameterExpression sourceParameter)
        {
            var visitedNodes       = new HashSet <ModelConfigurationNode>();
            var processedNodes     = new HashSet <ModelConfigurationNode>();
            var mutatorExpressions = new List <Expression>();
            var parameters         = new List <ParameterExpression> {
                destParameter
            };

            //Добавляем алиас для случая, когда билдим жиромутатор для поддерева, чтобы заменить путь до корня на реальный параметр выражения
            var aliases = new List <KeyValuePair <Expression, Expression> > {
                new KeyValuePair <Expression, Expression>(destParameter, node.Path)
            };
            var invariantParameters = new List <ParameterExpression>();

            if (sourceParameter != null)
            {
                parameters.Add(sourceParameter);
                invariantParameters.Add(sourceParameter);
            }

            node.BuildTreeMutator(null, node, aliases, mutatorExpressions, visitedNodes, processedNodes, mutatorExpressions, invariantParameters);

            //Оптимизации
            mutatorExpressions = mutatorExpressions.SplitToBatches(parameters.ToArray());
            mutatorExpressions.Add(Expression.Empty());
            Expression body = Expression.Block(mutatorExpressions);

            body = body.ExtractLoopInvariantFatExpressions(invariantParameters, expression => expression);

            //Далее параметры мутаторов подменяются на параметры итогового выражения
            foreach (var actualParameter in body.ExtractParameters())
            {
                var expectedParameter = parameters.Single(p => p.Type == actualParameter.Type);
                if (actualParameter != expectedParameter)
                {
                    body = new ParameterReplacer(actualParameter, expectedParameter).Visit(body);
                }
            }

            var result = Expression.Lambda(body, parameters);

            return(result);
        }