public override DynamicMetaObject Bind(DynamicMetaObject target, DynamicMetaObject[] args) { var restrictions = BindingRestrictions.Empty; Expression target_expr; object target_value; BinderHelpers.TargetAsObject(target, out target_expr, out target_value, ref restrictions); string fldName; Expression value; ResolveArgs(args, ref restrictions, out fldName, out value); var runtime_type = target_value.GetType(); // if (target_expr.Type != runtime_type) { restrictions = restrictions.Merge(BindingRestrictions.GetTypeRestriction(target_expr, runtime_type)); target_expr = Expression.Convert(target_expr, runtime_type); } // var setter = BinderHelpers.BindField(runtime_type.GetPhpTypeInfo(), _classContext, target_expr, fldName, null, _access, value); if (setter != null) { // return(new DynamicMetaObject(setter, restrictions)); } // field not found throw new NotImplementedException(); }
bool TryBindArgument(int srcarg, Type targetType, out Expression expr) { var args = _args; if (srcarg >= 0 && srcarg < args.Length) { // skip RuntimeChain's srcarg = MapToArgsIndex(srcarg); // if (srcarg < args.Length) { expr = args[srcarg]; // apply the runtime chain: if (srcarg + 1 < args.Length) { BinderHelpers.TryAppendRuntimeChain(ref expr, args[srcarg + 1], _ctx, _classContext, targetType == typeof(PhpAlias)); } // if (targetType != null) { expr = ConvertExpression.Bind(expr, targetType, _ctx); } // return(true); } } // not provided expr = null; return(false); }
public override DynamicMetaObject Bind(DynamicMetaObject target, DynamicMetaObject[] args) { var restrictions = BindingRestrictions.Empty; PhpTypeInfo phptype; Expression target_expr; // var ctx = args[0]; var fldName = ResolveName(args, ref restrictions); // if (target.LimitType == typeof(PhpTypeInfo)) // static field { target_expr = null; phptype = (PhpTypeInfo)target.Value; // restrictions = restrictions.Merge(BindingRestrictions.GetInstanceRestriction(target.Expression, phptype)); } else { var isobject = BinderHelpers.TryTargetAsObject(target, out DynamicMetaObject instance); restrictions = restrictions.Merge(instance.Restrictions); if (isobject == false) { var defaultexpr = ConvertExpression.BindDefault(_returnType); if (!_access.Quiet()) { // PhpException.VariableMisusedAsObject(target, _access.ReadRef) var throwcall = Expression.Call(typeof(PhpException), "VariableMisusedAsObject", Array.Empty <Type>(), ConvertExpression.BindToValue(target.Expression), Expression.Constant(_access.EnsureAlias())); defaultexpr = Expression.Block(throwcall, defaultexpr); } return(new DynamicMetaObject(defaultexpr, restrictions)); } phptype = instance.RuntimeType.GetPhpTypeInfo(); target_expr = target_expr = Expression.Convert(instance.Expression, instance.RuntimeType); } Debug.Assert(IsClassConst == (target_expr == null)); // var getter = IsClassConst ? BinderHelpers.BindClassConstant(phptype, _classContext, fldName, ctx.Expression) : BinderHelpers.BindField(phptype, _classContext, target_expr, fldName, ctx.Expression, _access, null); if (getter != null) { // return(new DynamicMetaObject(ConvertExpression.Bind(getter, _returnType, ctx.Expression), restrictions)); } // field not found throw new NotImplementedException(); }
protected override MethodBase[] ResolveMethods(DynamicMetaObject ctx, PhpTypeInfo tinfo, DynamicMetaObject nameExpr, ref DynamicMetaObject target, IList <DynamicMetaObject> args, ref BindingRestrictions restrictions) { // resolve target expression: Expression target_expr; object target_value; BinderHelpers.TargetAsObject(target, out target_expr, out target_value, ref restrictions); // (NULL)-> if (target_value == null) { return(null); // no methods } // target restrictions if (target_value != null && !target_expr.Type.GetTypeInfo().IsSealed) { restrictions = restrictions.Merge(BindingRestrictions.GetTypeRestriction(target_expr, target_value.GetType())); target_expr = Expression.Convert(target_expr, target_value.GetType()); } target = new DynamicMetaObject(target_expr, target.Restrictions, target_value); string name = _name ?? (string)nameExpr.Value; // candidates: var candidates = target.RuntimeType.GetPhpTypeInfo().SelectRuntimeMethods(name).SelectVisible(_classCtx).ToList(); return(candidates.ToArray()); }
protected override Expression BindMissingMethod(CallSiteContext bound) { if (bound.TargetType == null) // already reported - class cannot be found { return(ConvertExpression.BindDefault(this.ReturnType)); } var call = BinderHelpers.FindMagicMethod(bound.TargetType, (bound.TargetInstance == null) ? TypeMethods.MagicMethods.__callstatic : TypeMethods.MagicMethods.__call); if (call != null) { var name_expr = (_name != null) ? Expression.Constant(_name) : bound.IndirectName; // T.__callStatic(name, array) var call_args = new Expression[] { name_expr, BinderHelpers.NewPhpArray(bound.Arguments), }; return(OverloadBinder.BindOverloadCall(_returnType, bound.TargetInstance, call.Methods, bound.Context, call_args, true, lateStaticType: bound.TargetType)); } // return(base.BindMissingMethod(bound)); }
protected override Expression BindMissingMethod(CallSiteContext bound) { var name_expr = (_name != null) ? Expression.Constant(_name) : bound.IndirectName; // resolve target expression: var isobject = bound.TargetType != null; if (isobject == false) { /* Template: * PhpException.MethodOnNonObject(name_expr); // aka PhpException.Throw(Error, method_called_on_non_object, name_expr) * return NULL; */ var throwcall = Expression.Call(typeof(PhpException), "MethodOnNonObject", Array.Empty <Type>(), ConvertExpression.Bind(name_expr, typeof(string), bound.Context)); return(Expression.Block(throwcall, ConvertExpression.BindDefault(this.ReturnType))); } var call = BinderHelpers.FindMagicMethod(bound.TargetType, TypeMethods.MagicMethods.__call); if (call != null) { // target.__call(name, array) var call_args = new Expression[] { name_expr, BinderHelpers.NewPhpArray(bound.Arguments), }; return(OverloadBinder.BindOverloadCall(_returnType, bound.TargetInstance, call.Methods, bound.Context, call_args, false)); } return(base.BindMissingMethod(bound)); }
public override DynamicMetaObject Bind(DynamicMetaObject target, DynamicMetaObject[] args) { bool hasTargetInstance = (target.LimitType != typeof(TargetTypeParam)); var bound = new CallSiteContext(!hasTargetInstance) { ClassContext = _classContext, Name = _name } .ProcessArgs(target, args, hasTargetInstance); if (hasTargetInstance) { var isobject = bound.TargetType != null; if (isobject == false) { var defaultexpr = ConvertExpression.BindDefault(_returnType); if (!_access.Quiet()) { // PhpException.VariableMisusedAsObject(target, _access.ReadRef) var throwcall = BinderHelpers.VariableMisusedAsObject(target.Expression, _access.EnsureAlias()); defaultexpr = Expression.Block(throwcall, defaultexpr); } return(new DynamicMetaObject(defaultexpr, bound.Restrictions)); } // instance := (T)instance bound.TargetInstance = Expression.Convert(bound.TargetInstance, bound.TargetType.Type); } Debug.Assert(IsClassConst ? (bound.TargetInstance == null) : true); // var getter = IsClassConst ? BinderHelpers.BindClassConstant(bound.TargetType, bound.ClassContext, bound.Name, bound.Context) : BinderHelpers.BindField(bound.TargetType, bound.ClassContext, bound.TargetInstance, bound.Name, bound.Context, _access, null); if (getter != null) { // return(new DynamicMetaObject(ConvertExpression.Bind(getter, _returnType, bound.Context), bound.Restrictions)); } if (IsClassConst) { // error: constant not defined // ... } // unreachable: property not found throw new InvalidOperationException($"{bound.TargetType.Name}::{bound.Name} could not be resolved."); }
public override DynamicMetaObject Bind(DynamicMetaObject target, DynamicMetaObject[] args) { var restrictions = BindingRestrictions.Empty; Expression target_expr; PhpTypeInfo phptype; // string fldName; Expression value; ResolveArgs(args, ref restrictions, out fldName, out value); var ctx = args[args.Length - 1]; // if (target.LimitType == typeof(PhpTypeInfo)) // static field { target_expr = null; phptype = (PhpTypeInfo)target.Value; } else { object target_value; BinderHelpers.TargetAsObject(target, out target_expr, out target_value, ref restrictions); var runtime_type = target_value.GetType(); // if (target_expr.Type != runtime_type) { restrictions = restrictions.Merge(BindingRestrictions.GetTypeRestriction(target_expr, runtime_type)); target_expr = Expression.Convert(target_expr, runtime_type); } phptype = runtime_type.GetPhpTypeInfo(); } // var setter = BinderHelpers.BindField(phptype, _classContext, target_expr, fldName, ctx.Expression, _access, value); if (setter != null) { // return(new DynamicMetaObject(setter, restrictions)); } // field not found throw new NotImplementedException(); }
protected override Expression BindMissingMethod(CallSiteContext bound) { var type = bound.TargetType; if (type == null) // already reported - class cannot be found { return(ConvertExpression.BindDefault(this.ReturnType)); } if (bound.TargetInstance != null && bound.CurrentTargetInstance != null) // it has been checked it is a subclass of TargetType { // ensure current scope's __call() is favoured over the specified class type = bound.CurrentTargetInstance.GetPhpTypeInfo(); } var call = BinderHelpers.FindMagicMethod(type, (bound.TargetInstance == null) ? TypeMethods.MagicMethods.__callstatic : TypeMethods.MagicMethods.__call); if (call != null) { Expression[] call_args; var name_expr = (_name != null) ? Expression.Constant(_name) : bound.IndirectName; if (call.Methods.All(IsClrMagicCallWithParams)) { // Template: target.__call(name, arg1, arg2, ...) // flatterns the arguments: call_args = ArrayUtils.AppendRange(name_expr, bound.Arguments); } else { // Template: target.__call(name, array) // regular PHP behavior: call_args = new Expression[] { name_expr, BinderHelpers.NewPhpArray(bound.Arguments, bound.Context, bound.ClassContext), }; } return(OverloadBinder.BindOverloadCall(_returnType, bound.TargetInstance, call.Methods, bound.Context, call_args, isStaticCallSyntax: true, lateStaticType: bound.TargetType, classContext: bound.ClassContext)); } // return(base.BindMissingMethod(bound)); }
public override DynamicMetaObject Bind(DynamicMetaObject target, DynamicMetaObject[] args) { var restrictions = BindingRestrictions.Empty; PhpTypeInfo phptype; Expression target_expr; // var fldName = ResolveName(args, ref restrictions); // if (target.LimitType == typeof(PhpTypeInfo)) // static field { target_expr = null; phptype = (PhpTypeInfo)target.Value; // restrictions = restrictions.Merge(BindingRestrictions.GetInstanceRestriction(target.Expression, target_expr)); } else { // instance field object target_value; BinderHelpers.TargetAsObject(target, out target_expr, out target_value, ref restrictions); var runtime_type = target_value.GetType(); // if (target_expr.Type != runtime_type) { restrictions = restrictions.Merge(BindingRestrictions.GetTypeRestriction(target_expr, runtime_type)); target_expr = Expression.Convert(target_expr, runtime_type); } phptype = runtime_type.GetPhpTypeInfo(); } // var getter = BinderHelpers.BindField(phptype, _classContext, target_expr, fldName, null, _access, null); if (getter != null) { // return(new DynamicMetaObject(ConvertExpression.Bind(getter, _returnType), restrictions)); } // field not found throw new NotImplementedException(); }
public override DynamicMetaObject Bind(DynamicMetaObject target, DynamicMetaObject[] args) { bool hasTargetInstance = (target.LimitType != typeof(TargetTypeParam)); var bound = new CallSiteContext() { ClassContext = _classContext, Name = _name } .ProcessArgs(target, args, hasTargetInstance); if (hasTargetInstance) { var isobject = bound.TargetType != null; if (isobject == false) { var defaultexpr = ConvertExpression.BindDefault(_returnType); if (!_access.Quiet()) { // PhpException.VariableMisusedAsObject(target, _access.ReadRef) var throwcall = Expression.Call(typeof(PhpException), "VariableMisusedAsObject", Array.Empty <Type>(), ConvertExpression.BindToValue(target.Expression), Expression.Constant(_access.EnsureAlias())); defaultexpr = Expression.Block(throwcall, defaultexpr); } return(new DynamicMetaObject(defaultexpr, bound.Restrictions)); } // instance := (T)instance bound.TargetInstance = Expression.Convert(bound.TargetInstance, bound.TargetType.Type.AsType()); } Debug.Assert(IsClassConst ? (bound.TargetInstance == null) : true); // var getter = IsClassConst ? BinderHelpers.BindClassConstant(bound.TargetType, bound.ClassContext, bound.Name, bound.Context) : BinderHelpers.BindField(bound.TargetType, bound.ClassContext, bound.TargetInstance, bound.Name, bound.Context, _access, null); if (getter != null) { // return(new DynamicMetaObject(ConvertExpression.Bind(getter, _returnType, bound.Context), bound.Restrictions)); } // field not found throw new NotImplementedException(); }
void ProcessTarget(DynamicMetaObject target) { Debug.Assert(target != null); if (BinderHelpers.TryTargetAsObject(target, out target)) { this.AddRestriction(target.Restrictions); this.TargetInstance = target.Expression; this.CurrentTargetInstance = target.Value; if (this.TargetType == null && target.Value != null) { this.TargetType = target.Value.GetPhpTypeInfo(); } } }
public override Expression BindArgsCount() { int count = 0; foreach (var x in _args) { if (!BinderHelpers.IsRuntimeChain(x.Type)) { count++; } } // return(Expression.Constant(count, typeof(int))); }
static int MandatoryParametersCount(ParameterInfo[] ps, int from) { // gets count of parameters that are mandatory (last parameter that does not have a default value(is not optional)) int lastmandatory = -1; for (int i = from; i < ps.Length; i++) { if (BinderHelpers.IsMandatoryParameter(ps[i])) { lastmandatory = i; } } return(lastmandatory - from + 1); }
public sealed override DynamicMetaObject Bind(DynamicMetaObject target, DynamicMetaObject[] args) { var bound = CreateContext().ProcessArgs(target, args, HasTarget); Expression invocation; // var methods = ResolveMethods(bound); if (methods != null && methods.Length != 0) { // late static bound type, 'static' if available, otherwise the target type var lateStaticTypeArg = (object)bound.LateStaticType ?? bound.TargetType; if (bound.HasArgumentUnpacking) { var args_var = Expression.Variable(typeof(PhpValue[]), "args_array"); /* * args_var = ArgumentsToArray() * call(...args_var...) */ invocation = Expression.Block(new[] { args_var }, Expression.Assign(args_var, BinderHelpers.UnpackArgumentsToArray(methods, bound.Arguments, bound.Context, bound.ClassContext)), OverloadBinder.BindOverloadCall(_returnType, bound.TargetInstance, methods, bound.Context, args_var, isStaticCallSyntax: bound.IsStaticSyntax, lateStaticType: lateStaticTypeArg) ); } else { invocation = OverloadBinder.BindOverloadCall(_returnType, bound.TargetInstance, methods, bound.Context, bound.Arguments, isStaticCallSyntax: bound.IsStaticSyntax, lateStaticType: lateStaticTypeArg, classContext: bound.ClassContext); } } else { invocation = BindMissingMethod(bound); } // TODO: by alias or by value return(new DynamicMetaObject(invocation, bound.Restrictions)); }
protected override Expression BindMissingMethod(CallSiteContext bound) { var name_expr = (_name != null) ? Expression.Constant(_name) : bound.IndirectName; // resolve target expression: var isobject = bound.TargetType != null; if (isobject == false) { /* Template: * PhpException.MethodOnNonObject(name_expr); * return NULL; */ var throwcall = Expression.Call(typeof(PhpException), "MethodOnNonObject", Array.Empty <Type>(), ConvertExpression.Bind(name_expr, typeof(string), bound.Context)); return(Expression.Block(throwcall, ConvertExpression.BindDefault(this.ReturnType))); } var call = BinderHelpers.FindMagicMethod(bound.TargetType, TypeMethods.MagicMethods.__call); if (call != null) { Expression[] call_args; if (call.Methods.All(IsClrMagicCallWithParams)) { // Template: target.__call(name, arg1, arg2, ...) // flatterns the arguments: call_args = ArrayUtils.AppendRange(name_expr, bound.Arguments); } else { // Template: target.__call(name, array) // regular PHP behavior: call_args = new Expression[] { name_expr, BinderHelpers.NewPhpArray(bound.Arguments, bound.Context, bound.ClassContext), }; } return(OverloadBinder.BindOverloadCall(_returnType, bound.TargetInstance, call.Methods, bound.Context, call_args, false)); } return(base.BindMissingMethod(bound)); }
int MapToArgsIndex(int srcarg) { var args = _args; if (srcarg > 0 && srcarg < args.Length) // [0] is never IRuntimeChain { // skip RuntimeChain's for (int i = 1; i <= srcarg && i < args.Length; i++) { if (BinderHelpers.IsRuntimeChain(args[i].Type)) { ++srcarg; } } } return(srcarg); }
protected override MethodBase[] ResolveMethods(DynamicMetaObject ctx, ref DynamicMetaObject target, IList <DynamicMetaObject> args, ref BindingRestrictions restrictions) { if (target.Value == null) { Combine(ref restrictions, BindingRestrictions.GetInstanceRestriction(target.Expression, Expression.Constant(null))); // TODO: Err instance is null return(null); } // resolve target expression: Expression target_expr; object target_value; BinderHelpers.TargetAsObject(target, out target_expr, out target_value, ref restrictions); // target restrictions if (!target_expr.Type.GetTypeInfo().IsSealed) { restrictions = restrictions.Merge(BindingRestrictions.GetTypeRestriction(target_expr, target_value.GetType())); target_expr = Expression.Convert(target_expr, target_value.GetType()); } target = new DynamicMetaObject(target_expr, target.Restrictions, target_value); string name = _name; if (_name == null) { // indirect function name Debug.Assert(args.Count >= 1 && args[0].LimitType == typeof(string)); Debug.Assert(args[0].Value is string); restrictions = restrictions.Merge(BindingRestrictions.GetExpressionRestriction(Expression.Equal(args[0].Expression, Expression.Constant(args[0].Value)))); // args[0] == "VALUE" name = (string)args[0].Value; args.RemoveAt(0); } // candidates: var candidates = target.RuntimeType.SelectCandidates().SelectByName(name).SelectVisible(_classCtx).ToList(); return(candidates.ToArray()); }
protected override Expression BindMissingMethod(DynamicMetaObject ctx, PhpTypeInfo tinfo, DynamicMetaObject nameMeta, DynamicMetaObject target, IList <DynamicMetaObject> args, ref BindingRestrictions restrictions) { var call = BinderHelpers.FindMagicMethod(tinfo, target == null ? TypeMethods.MagicMethods.__callstatic : TypeMethods.MagicMethods.__call); if (call != null) { var name_expr = (_name != null) ? Expression.Constant(_name) : nameMeta?.Expression; // T.__callStatic(name, array) var call_args = new Expression[] { name_expr, BinderHelpers.NewPhpArray(ctx.Expression, args.Select(a => a.Expression)), }; return(OverloadBinder.BindOverloadCall(_returnType, target?.Expression, call.Methods, ctx.Expression, call_args)); } // return(base.BindMissingMethod(ctx, tinfo, nameMeta, target, args, ref restrictions)); }
protected override MethodBase[] ResolveMethods(DynamicMetaObject ctx, PhpTypeInfo tinfo, DynamicMetaObject nameExpr, ref DynamicMetaObject target, IList <DynamicMetaObject> args, ref BindingRestrictions restrictions) { // resolve target expression: var isobject = BinderHelpers.TryTargetAsObject(target, out DynamicMetaObject instance); restrictions = restrictions.Merge(instance.Restrictions); target = new DynamicMetaObject(Expression.Convert(instance.Expression, instance.RuntimeType), target.Restrictions, instance.Value); if (isobject == false) { return(null); // no methods } string name = _name ?? (string)nameExpr.Value; // candidates: var candidates = target.RuntimeType.GetPhpTypeInfo().SelectRuntimeMethods(name).SelectVisible(_classCtx).ToList(); return(candidates.ToArray()); }
protected override Expression BindMissingMethod(DynamicMetaObject ctx, PhpTypeInfo tinfo, DynamicMetaObject nameMeta, DynamicMetaObject target, IList <DynamicMetaObject> args, ref BindingRestrictions restrictions) { var name_expr = (_name != null) ? Expression.Constant(_name) : nameMeta?.Expression; // resolve target expression: Expression target_expr; object target_value; BinderHelpers.TargetAsObject(target, out target_expr, out target_value, ref restrictions); if (target_value == null) { /* Template: * PhpException.MethodOnNonObject(name_expr); // aka PhpException.Throw(Error, method_called_on_non_object, name_expr) * return NULL; */ var throwcall = Expression.Call(typeof(PhpException), "MethodOnNonObject", Array.Empty <Type>(), ConvertExpression.Bind(name_expr, typeof(string), ctx.Expression)); return(Expression.Block(throwcall, ConvertExpression.BindDefault(this.ReturnType))); } Debug.Assert(ReflectionUtils.IsClassType(target_value.GetType().GetTypeInfo())); tinfo = target_value.GetPhpTypeInfo(); var call = BinderHelpers.FindMagicMethod(tinfo, TypeMethods.MagicMethods.__call); if (call != null) { // target.__call(name, array) var call_args = new Expression[] { name_expr, BinderHelpers.NewPhpArray(ctx.Expression, args.Select(a => a.Expression)), }; return(OverloadBinder.BindOverloadCall(_returnType, target.Expression, call.Methods, ctx.Expression, call_args)); } return(base.BindMissingMethod(ctx, tinfo, nameMeta, target, args, ref restrictions)); }
public override DynamicMetaObject Bind(DynamicMetaObject target, DynamicMetaObject[] args) { bool hasTargetInstance = (target.LimitType != typeof(TargetTypeParam)); var bound = new CallSiteContext(!hasTargetInstance) { ClassContext = _classContext, Name = _name, } .ProcessArgs(target, args, hasTargetInstance); // if (hasTargetInstance) { var isobject = bound.TargetType != null; if (isobject == false) { // VariableMisusedAsObject return(new DynamicMetaObject( BinderHelpers.VariableMisusedAsObject(target.Expression, false), bound.Restrictions)); } // instance := (T)instance bound.TargetInstance = Expression.Convert(bound.TargetInstance, bound.TargetType.Type.AsType()); } // var setter = BinderHelpers.BindField(bound.TargetType, bound.ClassContext, bound.TargetInstance, bound.Name, bound.Context, _access, bound.Arguments.FirstOrDefault()); if (setter != null) { // return(new DynamicMetaObject(setter, bound.Restrictions)); } // field not found throw new NotImplementedException(); }
protected override Expression BindMissingMethod(DynamicMetaObject ctx, DynamicMetaObject target, IList <DynamicMetaObject> args, ref BindingRestrictions restrictions) { var tinfo = target.RuntimeType.GetPhpTypeInfo(); var call = (PhpMethodInfo)tinfo.DeclaredMethods[TypeMethods.MagicMethods.__call]; if (call != null) { if (_name == null) { throw new NotImplementedException(); } // target.__call(name, array) var call_args = new Expression[] { Expression.Constant(_name), BinderHelpers.NewPhpArray(ctx.Expression, args.Select(a => a.Expression)), }; return(OverloadBinder.BindOverloadCall(_returnType, target.Expression, call.Methods, ctx.Expression, call_args)); } return(base.BindMissingMethod(ctx, target, args, ref restrictions)); }
/// <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)); }