상속: System.Linq.Expressions.ExpressionVisitor
예제 #1
0
        // Creates a GeneratorLambda as a lambda containing a parameterless
        // generator. Because we want parameters to be captured by value and
        // not as variables, we have to do a transformation more like this:
        ///
        //    static IEnumerable<int> Foo(int count) {
        //        count *= 2;
        //        for (int i = 0; i < count; i++) {
        //            yield return i;
        //        }
        //    }
        //
        // Becomes:
        //
        //    static IEnumerable<int> Foo(int count) {
        //        return generator {
        //            int __count = count;
        //            __count *= 2;
        //            for (int i = 0; i < __count; i++) {
        //                yield return i;
        //            }
        //        }
        //    }
        //
        // This involves a full rewrite, unfortunately.
        private static Expression TransformEnumerable(Expression body, ReadOnlyCollection <ParameterExpression> paramList)
        {
            if (paramList.Count == 0)
            {
                return(body);
            }
            int count = paramList.Count;
            var vars  = new ParameterExpression[count];
            var map   = new Dictionary <ParameterExpression, ParameterExpression>(count);
            var block = new Expression[count + 1];

            for (int i = 0; i < count; i++)
            {
                ParameterExpression param = paramList[i];
                vars[i] = Expression.Variable(param.Type, param.Name);
                map.Add(param, vars[i]);
                block[i] = Expression.Assign(vars[i], param);
            }
            block[count] = new LambdaParameterRewriter(map).Visit(body);
            return(Expression.Block(
                       new ReadOnlyCollection <ParameterExpression>(vars),
                       new ReadOnlyCollection <Expression>(block)
                       ));
        }
예제 #2
0
        /// <summary>
        /// Fixes up lambda body and parameters to match the signature of the given delegate if needed.
        /// </summary>
        /// <param name="delegateType"></param>
        private void EnsureSignature(Type delegateType)
        {
            System.Diagnostics.Debug.Assert(_params != null, "must have parameter list here");

            //paramMapping is the dictionary where we record how we want to map parameters
            //the key is the parameter, the value is the expression it should be redirected to
            //so far the parameter can only be redirected to itself (means no change needed) or to
            //a synthetic variable that is added to the lambda when the original parameter has no direct
            //parameter backing in the delegate signature
            // Example:
            //      delegate siganture      del(x, params y[])
            //      lambda signature        lambda(a, b, param n[])
            //
            //  for the situation above the mapping will be  <a, x>, <b, V1>, <n, V2>
            //  where V1 and V2 are synthetic variables and initialized as follows -  V1 = y[0] , V2 = {y[1], y[2],... y[n]}
            ParameterInfo[] delegateParams = delegateType.GetMethod("Invoke").GetParameters();

            bool delegateHasParamarray = delegateParams.Length > 0 && delegateParams[delegateParams.Length - 1].IsDefined(typeof(ParamArrayAttribute), false);
            bool lambdaHasParamarray   = ParamsArray != null;

            if (lambdaHasParamarray && !delegateHasParamarray)
            {
                throw new ArgumentException("paramarray lambdas must have paramarray delegate type");
            }

            int copy   = delegateHasParamarray ? delegateParams.Length - 1 : delegateParams.Length;
            int unwrap = _params.Count - copy;

            if (lambdaHasParamarray)
            {
                unwrap--;
            }

            // Lambda must have at least as many parameters as the delegate, not counting the paramarray
            if (unwrap < 0)
            {
                throw new ArgumentException("lambda does not have enough parameters");
            }

            // shortcircuit if no rewrite is needed.
            if (!delegateHasParamarray)
            {
                bool needRewrite = false;
                for (int i = 0; i < copy; i++)
                {
                    if (_params[i].Type != delegateParams[i].ParameterType)
                    {
                        needRewrite = true;
                    }
                }

                if (!needRewrite)
                {
                    return;
                }
            }

            List <ParameterExpression> newParams            = new List <ParameterExpression>(delegateParams.Length);
            List <ParameterExpression> backingVars          = new List <ParameterExpression>();
            List <Expression>          preambuleExpressions = new List <Expression>();
            Dictionary <ParameterExpression, ParameterExpression> paramMapping = new Dictionary <ParameterExpression, ParameterExpression>();

            for (int i = 0; i < copy; i++)
            {
                // map to a converted variable
                if (_params[i].Type != delegateParams[i].ParameterType)
                {
                    ParameterExpression newParameter    = Expression.Parameter(delegateParams[i].ParameterType, delegateParams[i].Name);
                    ParameterExpression mappedParameter = _params[i];
                    ParameterExpression backingVariable = Expression.Variable(mappedParameter.Type, mappedParameter.Name);

                    newParams.Add(newParameter);
                    backingVars.Add(backingVariable);
                    paramMapping.Add(mappedParameter, backingVariable);
                    preambuleExpressions.Add(
                        Expression.Assign(
                            backingVariable,
                            Expression.Convert(
                                newParameter,
                                mappedParameter.Type
                                )
                            )
                        );
                }
                else
                {
                    //use the same parameter expression
                    newParams.Add(_params[i]);
                    paramMapping.Add(_params[i], _params[i]);
                }
            }

            if (delegateHasParamarray)
            {
                ParameterInfo       delegateParamarrayPi = delegateParams[delegateParams.Length - 1];
                ParameterExpression delegateParamarray   = Expression.Parameter(delegateParamarrayPi.ParameterType, delegateParamarrayPi.Name);

                newParams.Add(delegateParamarray);

                //unwarap delegate paramarray into variables and map parameters to the variables
                for (int i = 0; i < unwrap; i++)
                {
                    ParameterExpression mappedParameter = _params[copy + i];
                    ParameterExpression backingVariable = Expression.Variable(mappedParameter.Type, mappedParameter.Name);

                    backingVars.Add(backingVariable);
                    paramMapping.Add(mappedParameter, backingVariable);
                    preambuleExpressions.Add(
                        Expression.Assign(
                            backingVariable,
                            AstUtils.Convert(
                                Expression.ArrayAccess(
                                    delegateParamarray,
                                    AstUtils.Constant(i)
                                    ),
                                mappedParameter.Type
                                )
                            )
                        );
                }

                //lambda's paramarray should get elements from the delegate paramarray after skipping those that we unwrapped.
                if (lambdaHasParamarray)
                {
                    ParameterExpression mappedParameter = _paramsArray;
                    ParameterExpression backingVariable = Expression.Variable(mappedParameter.Type, mappedParameter.Name);

                    backingVars.Add(backingVariable);
                    paramMapping.Add(mappedParameter, backingVariable);

                    // Call the helper
                    MethodInfo shifter = typeof(RuntimeHelpers).GetMethod("ShiftParamsArray");
                    shifter = shifter.MakeGenericMethod(delegateParamarrayPi.ParameterType.GetElementType());

                    preambuleExpressions.Add(
                        Expression.Assign(
                            backingVariable,
                            AstUtils.Convert(
                                Expression.Call(
                                    shifter,
                                    delegateParamarray,
                                    AstUtils.Constant(unwrap)
                                    ),
                                mappedParameter.Type
                                )
                            )
                        );
                }
            }


            Expression newBody = new LambdaParameterRewriter(paramMapping).Visit(_body);

            preambuleExpressions.Add(newBody);
            _body = Expression.Block(preambuleExpressions);

            _paramsArray = null;
            _locals.AddRange(backingVars);
            _params = newParams;

            for (int i = 0; i < _visibleVars.Count; i++)
            {
                ParameterExpression p = _visibleVars[i].Key as ParameterExpression;
                ParameterExpression v;
                if (p != null && paramMapping.TryGetValue(p, out v))
                {
                    _visibleVars[i] = new KeyValuePair <ParameterExpression, bool>(v, _visibleVars[i].Value);
                }
            }
        }