private Expression MakeSelect(CurrentDependencies current, LambdaExpression selector)
        {
            var subExtractor = new DependenciesExtractor(selector, parameters.Concat(selector.Parameters));

            LambdaExpression[] primarySubDependencies;
            LambdaExpression[] additionalSubDependencies;
            subExtractor.Extract(out primarySubDependencies, out additionalSubDependencies);
            var prefixCurrent = MakeLambda(GotoCurrent(current.Prefix));

            current.AddSubDependencies(prefixCurrent, additionalSubDependencies);

            if (primarySubDependencies.Length > 1)
            {
                return(Expression.NewArrayInit(typeof(object), primarySubDependencies.Select(exp => Expression.Convert(prefixCurrent.Merge(exp).Body, typeof(object)))));
            }

            var primarySubDependency = primarySubDependencies.SingleOrDefault();

            if (primarySubDependency == null)
            {
                return(Expression.NewArrayInit(typeof(object)));
            }
            if (!selector.ReturnType.IsAssignableFrom(primarySubDependency.ReturnType))
            {
                return(null);
            }

            if (!(primarySubDependency.Body is NewExpression))
            {
                return(new AnonymousTypeEliminator().Eliminate(prefixCurrent.Merge(primarySubDependency)).Body);
            }
            var newExpression = (NewExpression)primarySubDependency.Body;

            return(Expression.New(newExpression.Constructor, newExpression.Arguments.Select(arg => prefixCurrent.Merge(Expression.Lambda(arg, primarySubDependency.Parameters)).Body)));
        }
 private void DefaultMethodProcessor(MethodInfo method, CurrentDependencies current, Expression[] arguments)
 {
     foreach (var argument in arguments)
     {
         Visit(argument);
     }
 }
 private void DistinctMethodProcessor(MethodInfo method, CurrentDependencies current, Expression[] arguments)
 {
     if (arguments.Any())
     {
         throw new NotSupportedException("Distinct method with arguments is not supported");
     }
     current.AddDependency(MakeLambda(current.Prefix));
     current.ReplaceCurrentWithEach();
 }
        private void ProcessLinqCount(MethodInfo method, CurrentDependencies current, Expression[] arguments)
        {
            var selector = (LambdaExpression)arguments.SingleOrDefault();

            if (selector != null)
            {
                throw new NotSupportedException("Count method with predicate is not supported");
            }
            current.AddDependency(MakeLambda(current.Prefix));

            current.ReplaceCurrentWithEach();
        }
        private void ProcessExternalCurrent(MethodInfo method, CurrentDependencies current, Expression[] arguments)
        {
            var prefix = current.Prefix;

            if (prefix.IsAnonymousTypeCreation() || prefix.IsTupleCreation() || IsExternalCurrentCall(prefix))
            {
                return;
            }
            if (IsEachOrCurrentCall(prefix))
            {
                prefix = ((MethodCallExpression)prefix).Arguments[0];
            }
            current.Prefix = Expression.Call(DependenciesExtractorHelper.ExternalCurrentMethod.MakeGenericMethod(prefix.Type.GetItemType()), prefix);
        }
        private void ProcessLinqSum(MethodInfo method, CurrentDependencies current, Expression[] arguments)
        {
            var selector = (LambdaExpression)arguments.SingleOrDefault();

            if (selector != null)
            {
                current.Prefix = MakeSelect(current, selector);
            }
            else
            {
                current.Prefix = GotoEach(current.Prefix);
                current.AddDependency(MakeLambda(current.Prefix));
            }

            current.ReplaceCurrentWithEach();
        }
        private void ProcessLinqAggregate(MethodInfo method, CurrentDependencies current, Expression[] arguments)
        {
            switch (arguments.Length)
            {
            case 2:
            {
                var seed     = arguments[0];
                var selector = (LambdaExpression)arguments[1];
                current.AddDependency(MakeLambda(seed));
                current.Prefix = MakeSelect(current, Expression.Lambda(selector.Body, selector.Parameters[1]));
                current.ReplaceCurrentWithEach();
                break;
            }

            default:
                throw new NotSupportedException(string.Format("Method '{0}' is not supported", method));
            }
        }
        private void ProcessLinqWhere(MethodInfo method, CurrentDependencies current, Expression[] arguments)
        {
            if (current.Prefix.Type == typeof(string))
            {
                return;
            }
            var prefixCurrent = MakeLambda(GotoCurrent(current.Prefix));
            var predicate     = (LambdaExpression)arguments.SingleOrDefault();

            if (predicate == null)
            {
                current.AddDependency(prefixCurrent);
            }
            else
            {
                var subDependencies = predicate.ExtractDependencies(parameters.Concat(predicate.Parameters));
                current.AddSubDependencies(prefixCurrent, subDependencies);
            }
        }
        private void ProcessLinqFirst(MethodInfo method, CurrentDependencies current, Expression[] arguments)
        {
            if (current.Prefix.Type.IsArray && method.ReturnType == current.Prefix.Type.GetItemType())
            {
                current.Prefix = GotoEach(current.Prefix);
            }
            var predicate = (LambdaExpression)arguments.SingleOrDefault();

            if (predicate == null)
            {
                current.AddDependency(MakeLambda(current.Prefix));
            }
            else
            {
                var subDependencies = predicate.ExtractDependencies(parameters.Concat(predicate.Parameters));
                current.AddSubDependencies(MakeLambda(current.Prefix), subDependencies);
            }

            current.ReplaceCurrentWithEach();
        }
        private void ProcessLinqSelectMany(MethodInfo method, CurrentDependencies current, Expression[] arguments)
        {
            var collectionSelector = (LambdaExpression)arguments[0];
            var collection         = MakeSelect(current, collectionSelector);

            if (collection == null)
            {
                current.Prefix = null;
                return;
            }

            if (arguments.Length == 1)
            {
                current.Prefix = collection;
                return;
            }

            var resultSelector = (LambdaExpression)arguments[1];
            var subExtractor   = new DependenciesExtractor(resultSelector, parameters.Concat(resultSelector.Parameters));

            LambdaExpression[] primarySubDependencies;
            LambdaExpression[] additionalSubDependencies;
            subExtractor.Extract(out primarySubDependencies, out additionalSubDependencies);
            var prefixCurrent     = MakeLambda(GotoCurrent(current.Prefix));
            var collectionCurrent = MakeLambda(GotoCurrent(collection));

            current.AddSubDependencies(prefixCurrent, collectionCurrent, additionalSubDependencies);
            var primarySubDependency = primarySubDependencies.Single();

            if (!(primarySubDependency.Body is NewExpression))
            {
                current.Prefix = new AnonymousTypeEliminator().Eliminate(new ExpressionMerger(prefixCurrent, collectionCurrent).Merge(primarySubDependency)).Body;
            }
            else
            {
                var newExpression = (NewExpression)primarySubDependency.Body;
                current.Prefix = Expression.New(newExpression.Constructor, newExpression.Arguments.Select(arg => new ExpressionMerger(prefixCurrent, collectionCurrent).Merge(Expression.Lambda(arg, primarySubDependency.Parameters)).Body));
            }
        }
        private void ProcessLinqAny(CurrentDependencies current, Expression[] arguments, bool emptyPredicateIsDependency)
        {
            if (current.Prefix.Type == typeof(string))
            {
                return;
            }
            var prefixEach = MakeLambda(GotoEach(current.Prefix));
            var predicate  = (LambdaExpression)arguments.SingleOrDefault();

            if (predicate != null)
            {
                var subDependencies = predicate.ExtractDependencies(parameters.Concat(predicate.Parameters));
                current.AddSubDependencies(prefixEach, subDependencies);
            }
            else if (emptyPredicateIsDependency)
            {
                current.AddDependency(prefixEach);
            }

            current.Prefix = prefixEach.Body;
            current.ReplaceCurrentWithEach();
        }
 private void ProcessCurrentIndex(MethodInfo method, CurrentDependencies current, Expression[] arguments)
 {
     current.Prefix = Expression.ArrayLength(((MethodCallExpression)current.Prefix).Arguments.Single());
 }
 private void ProcessLinqSelect(MethodInfo method, CurrentDependencies current, Expression[] arguments)
 {
     current.Prefix = MakeSelect(current, (LambdaExpression)arguments.Single());
 }
 private void ProcessLinqToArray(MethodInfo method, CurrentDependencies current, Expression[] arguments)
 {
     ProcessLinqAny(current, arguments, false);
 }
 private void ProcessIndexer(MethodInfo method, CurrentDependencies current, Expression[] arguments)
 {
     current.Prefix = Expression.Call(current.Prefix, method, arguments);
 }
        private Expression VisitChain(Expression node)
        {
            var smithereens = node.SmashToSmithereens();

            if (smithereens.Last().IsStringLengthPropertyAccess())
            {
                smithereens = smithereens.Take(smithereens.Length - 1).ToArray();
            }
            if (smithereens[0].NodeType == ExpressionType.Constant)
            {
                return(Expression.NewArrayInit(typeof(object)));
            }
            if (smithereens[0].NodeType != ExpressionType.Parameter)
            {
                return(base.Visit(node));
            }
            var index = 0;

            while (index < smithereens.Length && smithereens[index].NodeType != ExpressionType.Call)
            {
                ++index;
            }
            var current = new CurrentDependencies
            {
                Prefix = smithereens[index - 1],
                AdditionalDependencies = new List <LambdaExpression>()
            };

            while (index < smithereens.Length && current.Prefix != null)
            {
                var shard      = smithereens[index];
                var prevPrefix = current.Prefix;
                switch (shard.NodeType)
                {
                case ExpressionType.Call:
                {
                    var methodCallExpression = (MethodCallExpression)shard;
                    var method    = methodCallExpression.Method;
                    var arguments = (method.IsExtension() ? methodCallExpression.Arguments.Skip(1) : methodCallExpression.Arguments).ToArray();
                    if (method.IsGenericMethod)
                    {
                        method = method.GetGenericMethodDefinition();
                    }
                    var methodProcessor = GetMethodProcessor(method);
                    if (methodProcessor == null)
                    {
                        throw new NotSupportedException("Method '" + method + "' is not supported");
                    }
                    methodProcessor(methodCallExpression.Method, current, arguments);
                    break;
                }

                case ExpressionType.MemberAccess:
                {
                    var member = ((MemberExpression)shard).Member;
                    if (!(current.Prefix is NewExpression))
                    {
                        current.Prefix = Expression.MakeMemberAccess(current.Prefix, member);
                    }
                    else
                    {
                        if (!current.Prefix.IsAnonymousTypeCreation() && !current.Prefix.IsTupleCreation())
                        {
                            throw new NotSupportedException("An anonymous type or a tuple creation expected but was " + ExpressionCompiler.DebugViewGetter(current.Prefix));
                        }
                        var          newExpression = (NewExpression)current.Prefix;
                        var          type          = newExpression.Type;
                        MemberInfo[] members;
                        if (member is PropertyInfo)
                        {
                            members = type.GetProperties(BindingFlags.Instance | BindingFlags.Public);
                        }
                        else
                        {
                            throw new NotSupportedException();
                        }
                        var i = Array.IndexOf(members, member);
                        if (i < 0 || i >= newExpression.Arguments.Count)
                        {
                            throw new InvalidOperationException();
                        }
                        var newArrayExpression = (NewArrayExpression)ClearConverts(newExpression.Arguments[i]);
                        current.Prefix = newArrayExpression.Expressions.Count == 1 ? ClearConverts(newArrayExpression.Expressions[0]) : newArrayExpression;
                    }
                }
                break;

                case ExpressionType.ArrayIndex:
                    if (current.Prefix.Type.IsArray)
                    {
                        current.Prefix = Expression.MakeBinary(ExpressionType.ArrayIndex, current.Prefix, ((BinaryExpression)shard).Right);
                    }
                    break;

                case ExpressionType.Convert:
                    if (current.Prefix.Type == typeof(object))
                    {
                        current.Prefix = Expression.Convert(current.Prefix, shard.Type);
                    }
                    break;

                default:
                    throw new InvalidOperationException("Node type '" + shard.NodeType + "' is not supported");
                }

                if (current.Prefix == null)
                {
                    current.Prefix = prevPrefix;
                    break;
                }

                ++index;
            }

            var primaryDependencies = new List <Expression>();

            if (current.Prefix != null)
            {
                if (current.Prefix is NewExpression)
                {
                    if (!current.Prefix.IsAnonymousTypeCreation() && !current.Prefix.IsTupleCreation())
                    {
                        primaryDependencies.AddRange(((NewExpression)current.Prefix).Arguments);
                    }
                    else
                    {
                        foreach (var expression in ((NewExpression)current.Prefix).Arguments.Select(ClearConverts))
                        {
                            primaryDependencies.AddRange(((NewArrayExpression)expression).Expressions.Select(ClearConverts));
                        }
                    }
                }
                else if (current.Prefix is NewArrayExpression)
                {
                    primaryDependencies.AddRange(((NewArrayExpression)current.Prefix).Expressions.Select(ClearConverts));
                }
                else if (IsPrimary(smithereens.Last()))
                {
                    primaryDependencies.Add(current.Prefix);
                }
            }

            dependencies.AddRange(current.AdditionalDependencies);

            return(Expression.NewArrayInit(typeof(object), primaryDependencies.Select(dependency => Expression.Convert(dependency, typeof(object)))));
        }