/// <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)); }
/// <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)); }