/// <summary> /// Helper method for generating a MetaObject which calls a /// specific method on DynamicObject that returns a result. /// /// args is either an array of arguments to be passed /// to the method as an object[] or NoArgs to signify that /// the target method takes no parameters. /// </summary> 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); }
/// <summary> /// Helper method for generating a MetaObject which calls a /// specific method on Dynamic that returns a result /// </summary> private DynamicMetaObject CallMethodWithResult(string methodName, DynamicMetaObjectBinder binder, Expression[] args, Fallback fallback) { return(CallMethodWithResult(methodName, binder, args, fallback, null)); }