Beispiel #1
0
        /// <summary>
        /// Creates expression with overload resolution.
        /// </summary>
        /// <param name="treturn">Expected return type.</param>
        /// <param name="target">Target instance in case of instance methods.</param>
        /// <param name="methods">List of methods to resolve overload.</param>
        /// <param name="ctx">Expression of current runtime context.</param>
        /// <param name="args">Provides arguments.</param>
        /// <param name="lateStaticType">Optional type used to statically invoke the method (late static type).</param>
        /// <returns>Expression representing overload call with resolution or <c>null</c> in case binding should be restarted with updated array of <paramref name="methods"/>.</returns>
        static Expression BindOverloadCall(Type treturn, Expression target, ref MethodBase[] methods, Expression ctx, ArgumentsBinder args, PhpTypeInfo lateStaticType = null)
        {
            if (methods == null || args == null)
            {
                throw new ArgumentNullException();
            }

            // overload resolution

            // order methods

            /*
             * cost1 = CostOf(m1)
             * cost2 = CostOf(m2)
             * ...
             * best = Min(cost1, .., costN)
             * if (cost1 == best) m1( ... )
             * ...
             * default(T) // unreachable
             */

            var locals = new List <ParameterExpression>();
            var body   = new List <Expression>();

            Expression invoke = Expression.Default(treturn);

            //
            if (methods.Length == 0)
            {
                throw new ArgumentException();    // no method to call
                // invoke = ERR
            }
            if (methods.Length == 1)
            {
                // just this piece of code is enough:
                invoke = ConvertExpression.Bind(BindCastToFalse(BinderHelpers.BindToCall(target, methods[0], ctx, args, lateStaticType), DoCastToFalse(methods[0], treturn)), treturn, ctx);
            }
            else
            {
                var list = new List <MethodCostInfo>();

                // collect arguments, that have same type across all provided methods => we don't have to check costof()
                var makeCostOf = DifferentArgumentTypeIndexes(methods); // parameters which costof() have to be calculated and compared to others

                // costX = CostOf(mX)
                foreach (var m in methods)
                {
                    ConversionCost mincost; // minimal cost resolved in compile time
                    var            expr_cost = BindCostOf(m, args, makeCostOf, out mincost);
                    if (mincost >= ConversionCost.NoConversion)
                    {
                        continue;   // we don't have to try this overload
                    }
                    var const_cost = expr_cost as ConstantExpression;
                    var cost_var   = Expression.Variable(typeof(ConversionCost), "cost" + list.Count);

                    if (const_cost == null)
                    {
                        body.Add(Expression.Assign(cost_var, expr_cost));   // costX = CostOf(m)
                    }

                    list.Add(new MethodCostInfo()
                    {
                        Method      = m,
                        CostExpr    = (Expression)const_cost ?? cost_var,
                        MinimalCost = mincost,
                    });
                }

                if (list.Count != 0)
                {
                    var minresolved = ConversionCost.Error;
                    foreach (var c in list.Where(c => c.ResolvedCost.HasValue))
                    {
                        minresolved = CostOf.Min(minresolved, c.ResolvedCost.Value);
                    }

                    if (list.All(c => c.ResolvedCost.HasValue))
                    {
                        for (int i = 0; i < list.Count; i++)
                        {
                            if (list[i].ResolvedCost.Value == minresolved)
                            {
                                list = new List <MethodCostInfo>()
                                {
                                    list[i]
                                };
                                break;
                            }
                        }
                    }
                    else
                    {
                        // if minimal cost is greater than some resolved cost, we can reduce it
                        if (minresolved < ConversionCost.Error)
                        {
                            for (int i = list.Count - 1; i >= 0; i--)
                            {
                                if (list[i].MinimalCost > minresolved)
                                {
                                    list.RemoveAt(i);
                                }
                            }
                        }
                    }
                }

                if (list.Count < methods.Length)
                {
                    // restart binding with reduced list
                    methods = list.Select(l => l.Method).ToArray();
                    return(null);
                }

                Debug.Assert(list.Count != 0);

                // declare costI local variables
                locals.AddRange(list.Select(l => l.CostExpr).OfType <ParameterExpression>());

                // best = Min( cost1, .., costN )
                var        expr_best     = Expression.Variable(typeof(ConversionCost), "best");
                var        min_cost_cost = typeof(CostOf).GetMethod("Min", typeof(ConversionCost), typeof(ConversionCost));
                Expression minexpr       = list[0].CostExpr;
                for (int i = 1; i < list.Count; i++)
                {
                    minexpr = Expression.Call(min_cost_cost, list[i].CostExpr, minexpr);
                }
                body.Add(Expression.Assign(expr_best, minexpr));
                locals.Add(expr_best);

                // switch over method costs
                for (int i = list.Count - 1; i >= 0; i--)
                {
                    // (best == costI) mI(...) : ...

                    var m     = list[i].Method;
                    var mcall = ConvertExpression.Bind(BindCastToFalse(BinderHelpers.BindToCall(target, m, ctx, args, lateStaticType), DoCastToFalse(m, treturn)), treturn, ctx);
                    invoke = Expression.Condition(Expression.Equal(expr_best, list[i].CostExpr), mcall, invoke);
                }
            }

            //
            body.Insert(0, args.CreatePreamble(locals));

            //
            body.Add(invoke);

            // return Block { ... ; invoke; }
            return(Expression.Block(treturn, locals, body));
        }
Beispiel #2
0
        /// <summary>
        /// Creates expression that computes cost of the method call with given arguments.
        /// </summary>
        /// <param name="method">Method to calculate the cost.</param>
        /// <param name="args">Arguments provider.</param>
        /// <param name="costofargs">Indexes of parameters which costof() have to be calculated.</param>
        /// <param name="minCost">Gets minimal compile-time cost of conversion.</param>
        /// <returns>Expression getting cost of conversion.</returns>
        static Expression BindCostOf(MethodBase method, ArgumentsBinder args, BitArray costofargs, out ConversionCost minCost)
        {
            if (method == null || args == null)
            {
                throw new ArgumentNullException();
            }

            var ps = method.GetParameters();

            minCost = ConversionCost.Pass;

            // method( {implicit}, {mandatory}, {optional+params} )

            var nimplicit  = ImplicitParametersCount(ps);
            var nmandatory = MandatoryParametersCount(ps, nimplicit);
            var noptional  = ps.Length - nimplicit - nmandatory;

            /*
             * var result = ConversionCost.Pass; // == 0
             *
             * result = CostOf(argv[0], T1) | CostOf(argv[1], T2) | ... CostOf(argv[nmandatory - 1], TN)    // ! BinaryOrCosts(...)
             * result |= (argc > expectedargs) ? TooManyArgs : Pass;
             * IF (noptional > 0) { ... }
             *
             * return result;
             */

            var expr_argc = args.BindArgsCount();
            int?argc_opt  = (expr_argc is ConstantExpression) ? (int?)((ConstantExpression)expr_argc).Value : null;

            // parameters cost
            var  block_cost = new List <Expression>();
            var  expr_costs = new List <Expression>();
            bool hasparams  = false;

            for (int im = 0; im < nmandatory + noptional; im++)
            {
                var p = ps[nimplicit + im];
                if (noptional != 0 && p.Position == ps.Length - 1 && p.IsParamsParameter())
                {
                    hasparams = true;

                    var element_type = p.ParameterType.GetElementType();

                    // for (int o = io + nmandatory; o < argc; o++) result |= CostOf(argv[o], p.ElementType)
                    if (argc_opt.HasValue)
                    {
                        for (; im < argc_opt.Value; im++)
                        {
                            expr_costs.Add(args.BindCostOf(im, element_type, false, false));
                        }
                    }
                    else
                    {
                        // just return DefaultValue (which is greater than Warning), for performance reasons
                        expr_costs.Add(Expression.Constant(ConversionCost.DefaultValue));
                    }

                    break;
                }

                //
                expr_costs.Add(args.BindCostOf(im, p.ParameterType, im < nmandatory, costofargs[im] == false));
            }

            if (hasparams == false)
            {
                // (argc > expectedargs) ? TooManyArgs : Pass
                expr_costs.Add(args.BindCostOfTooManyArgs(nmandatory + noptional));
            }

            // collect known costs
            foreach (var cc in expr_costs.OfType <ConstantExpression>().Select(x => (ConversionCost)x.Value))
            {
                minCost |= cc;
            }

            //
            return(CombineCosts(expr_costs));
        }