public static BindToCall ( |
||
instance | ||
method | ||
ctx | ||
args | OverloadBinder | |
Résultat |
/// <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)); }