private DynamicMetaObject BuildCallMethodWithResult(string methodName, DynamicMetaObjectBinder binder, Expression[] args, DynamicMetaObject fallbackResult, Fallback fallbackInvoke) {
if (!IsOverridden(methodName)) {
return fallbackResult;
}
//
// Build a new expression like:
// {
// object result;
// TryGetMember(payload, out result) ? fallbackInvoke(result) : fallbackResult
// }
//
var result = Expression.Parameter(typeof(object), null);
ParameterExpression callArgs = methodName != "TryBinaryOperation" ? Expression.Parameter(typeof(object[]), null) : Expression.Parameter(typeof(object), null);
var callArgsValue = GetConvertedArgs(args);
var resultMO = new DynamicMetaObject(result, BindingRestrictions.Empty);
// Need to add a conversion if calling TryConvert
if (binder.ReturnType != typeof(object)) {
Debug.Assert(binder is ConvertBinder && fallbackInvoke == null);
var convert = Expression.Convert(resultMO.Expression, binder.ReturnType);
// will always be a cast or unbox
Debug.Assert(convert.Method == null);
// Prepare a good exception message in case the convert will fail
string convertFailed = Strings.DynamicObjectResultNotAssignable(
"{0}",
this.Value.GetType(),
binder.GetType(),
binder.ReturnType
);
var checkedConvert = Expression.Condition(
Expression.TypeIs(resultMO.Expression, binder.ReturnType),
convert,
Expression.Throw(
Expression.New(typeof(InvalidCastException).GetConstructor(new Type[]{typeof(string)}),
Expression.Call(
typeof(string).GetMethod("Format", new Type[] {typeof(string), typeof(object)}),
Expression.Constant(convertFailed),
Expression.Condition(
Expression.Equal(resultMO.Expression, Expression.Constant(null)),
Expression.Constant("null"),
Expression.Call(
resultMO.Expression,
typeof(object).GetMethod("GetType")
),
typeof(object)
)
)
),
binder.ReturnType
),
binder.ReturnType
);
resultMO = new DynamicMetaObject(checkedConvert, resultMO.Restrictions);
}
if (fallbackInvoke != null) {
resultMO = fallbackInvoke(resultMO);
}
var callDynamic = new DynamicMetaObject(
Expression.Block(
new[] { result, callArgs },
methodName != "TryBinaryOperation" ? Expression.Assign(callArgs, Expression.NewArrayInit(typeof(object), callArgsValue)) : Expression.Assign(callArgs, callArgsValue[0]),
Expression.Condition(
Expression.Call(
GetLimitedSelf(),
typeof(DynamicObject).GetMethod(methodName),
BuildCallArgs(
binder,
args,
callArgs,
result
)
),
Expression.Block(
methodName != "TryBinaryOperation" ? ReferenceArgAssign(callArgs, args) : Expression.Empty(),
resultMO.Expression
),
fallbackResult.Expression,
binder.ReturnType
)
),
GetRestrictions().Merge(resultMO.Restrictions).Merge(fallbackResult.Restrictions)
);
return callDynamic;
}