public sealed override Expression Bind(object[] args, ReadOnlyCollection<ParameterExpression> parameters, LabelTarget returnLabel)
{
ContractUtils.RequiresNotNull(args, nameof(args));
ContractUtils.RequiresNotNull(parameters, nameof(parameters));
ContractUtils.RequiresNotNull(returnLabel, nameof(returnLabel));
if (args.Length == 0)
{
throw System.Linq.Expressions.Error.OutOfRange("args.Length", 1);
}
if (parameters.Count == 0)
{
throw System.Linq.Expressions.Error.OutOfRange("parameters.Count", 1);
}
if (args.Length != parameters.Count)
{
throw new ArgumentOutOfRangeException(nameof(args));
}
// Ensure that the binder's ReturnType matches CallSite's return
// type. We do this so meta objects and language binders can
// compose trees together without needing to insert converts.
Type expectedResult;
if (IsStandardBinder)
{
expectedResult = ReturnType;
if (returnLabel.Type != typeof(void) &&
!TypeUtils.AreReferenceAssignable(returnLabel.Type, expectedResult))
{
throw System.Linq.Expressions.Error.BinderNotCompatibleWithCallSite(expectedResult, this, returnLabel.Type);
}
}
else
{
// Even for non-standard binders, we have to at least make sure
// it works with the CallSite's type to build the return.
expectedResult = returnLabel.Type;
}
DynamicMetaObject target = DynamicMetaObject.Create(args[0], parameters[0]);
DynamicMetaObject[] metaArgs = CreateArgumentMetaObjects(args, parameters);
DynamicMetaObject binding = Bind(target, metaArgs);
if (binding == null)
{
throw System.Linq.Expressions.Error.BindingCannotBeNull();
}
Expression body = binding.Expression;
BindingRestrictions restrictions = binding.Restrictions;
// Ensure the result matches the expected result type.
if (expectedResult != typeof(void) &&
!TypeUtils.AreReferenceAssignable(expectedResult, body.Type))
{
//
// Blame the last person that handled the result: assume it's
// the dynamic object (if any), otherwise blame the language.
//
if (target.Value is IDynamicMetaObjectProvider)
{
throw System.Linq.Expressions.Error.DynamicObjectResultNotAssignable(body.Type, target.Value.GetType(), this, expectedResult);
}
else
{
throw System.Linq.Expressions.Error.DynamicBinderResultNotAssignable(body.Type, this, expectedResult);
}
}
// if the target is IDO, standard binders ask it to bind the rule so we may have a target-specific binding.
// it makes sense to restrict on the target's type in such cases.
// ideally IDO metaobjects should do this, but they often miss that type of "this" is significant.
if (IsStandardBinder && args[0] as IDynamicMetaObjectProvider != null)
{
if (restrictions == BindingRestrictions.Empty)
{
throw System.Linq.Expressions.Error.DynamicBindingNeedsRestrictions(target.Value.GetType(), this);
}
}
// Add the return
if (body.NodeType != ExpressionType.Goto)
{
body = Expression.Return(returnLabel, body);
}
// Finally, add restrictions
if (restrictions != BindingRestrictions.Empty)
{
body = Expression.IfThen(restrictions.ToExpression(), body);
}
return body;
}