1 /* **************************************************************************** 2 * 3 * Copyright (c) Microsoft Corporation. 4 * 5 * This source code is subject to terms and conditions of the Apache License, Version 2.0. A 6 * copy of the license can be found in the License.html file at the root of this distribution. If 7 * you cannot locate the Apache License, Version 2.0, please send an email to 8 * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound 9 * by the terms of the Apache License, Version 2.0. 10 * 11 * You must not remove this notice, or any other, from this software. 12 * 13 * 14 * ***************************************************************************/ 15 16 #if CLR2 17 using Microsoft.Scripting.Ast; 18 using Microsoft.Scripting.Ast.Compiler; 19 #else 20 using System.Linq.Expressions; 21 using System.Linq.Expressions.Compiler; 22 #endif 23 24 #if SILVERLIGHT 25 using System.Core; 26 #else 27 using System.Runtime.Remoting; 28 #endif 29 30 using System.Collections.ObjectModel; 31 using System.Diagnostics; 32 using System.Dynamic.Utils; 33 using System.Runtime.CompilerServices; 34 35 namespace System.Dynamic { 36 /// <summary> 37 /// The dynamic call site binder that participates in the <see cref="DynamicMetaObject"/> binding protocol. 38 /// </summary> 39 /// <remarks> 40 /// The <see cref="CallSiteBinder"/> performs the binding of the dynamic operation using the runtime values 41 /// as input. On the other hand, the <see cref="DynamicMetaObjectBinder"/> participates in the <see cref="DynamicMetaObject"/> 42 /// binding protocol. 43 /// </remarks> 44 public abstract class DynamicMetaObjectBinder : CallSiteBinder { 45 46 #region Public APIs 47 48 /// <summary> 49 /// Initializes a new instance of the <see cref="DynamicMetaObjectBinder"/> class. 50 /// </summary> DynamicMetaObjectBinder()51 protected DynamicMetaObjectBinder() { 52 } 53 54 /// <summary> 55 /// The result type of the operation. 56 /// </summary> 57 public virtual Type ReturnType { 58 get { return typeof(object); } 59 } 60 61 /// <summary> 62 /// Performs the runtime binding of the dynamic operation on a set of arguments. 63 /// </summary> 64 /// <param name="args">An array of arguments to the dynamic operation.</param> 65 /// <param name="parameters">The array of <see cref="ParameterExpression"/> instances that represent the parameters of the call site in the binding process.</param> 66 /// <param name="returnLabel">A LabelTarget used to return the result of the dynamic binding.</param> 67 /// <returns> 68 /// An Expression that performs tests on the dynamic operation arguments, and 69 /// performs the dynamic operation if the tests are valid. If the tests fail on 70 /// subsequent occurrences of the dynamic operation, Bind will be called again 71 /// to produce a new <see cref="Expression"/> for the new argument types. 72 /// </returns> Bind(object[] args, ReadOnlyCollection<ParameterExpression> parameters, LabelTarget returnLabel)73 public sealed override Expression Bind(object[] args, ReadOnlyCollection<ParameterExpression> parameters, LabelTarget returnLabel) { 74 ContractUtils.RequiresNotNull(args, "args"); 75 ContractUtils.RequiresNotNull(parameters, "parameters"); 76 ContractUtils.RequiresNotNull(returnLabel, "returnLabel"); 77 if (args.Length == 0) { 78 throw Error.OutOfRange("args.Length", 1); 79 } 80 if (parameters.Count == 0) { 81 throw Error.OutOfRange("parameters.Count", 1); 82 } 83 if (args.Length != parameters.Count) { 84 throw new ArgumentOutOfRangeException("args"); 85 } 86 87 // Ensure that the binder's ReturnType matches CallSite's return 88 // type. We do this so meta objects and language binders can 89 // compose trees together without needing to insert converts. 90 Type expectedResult; 91 if (IsStandardBinder) { 92 expectedResult = ReturnType; 93 94 if (returnLabel.Type != typeof(void) && 95 !TypeUtils.AreReferenceAssignable(returnLabel.Type, expectedResult)) { 96 throw Error.BinderNotCompatibleWithCallSite(expectedResult, this, returnLabel.Type); 97 } 98 } else { 99 // Even for non-standard binders, we have to at least make sure 100 // it works with the CallSite's type to build the return. 101 expectedResult = returnLabel.Type; 102 } 103 104 DynamicMetaObject target = DynamicMetaObject.Create(args[0], parameters[0]); 105 DynamicMetaObject[] metaArgs = CreateArgumentMetaObjects(args, parameters); 106 107 DynamicMetaObject binding = Bind(target, metaArgs); 108 109 if (binding == null) { 110 throw Error.BindingCannotBeNull(); 111 } 112 113 Expression body = binding.Expression; 114 BindingRestrictions restrictions = binding.Restrictions; 115 116 // Ensure the result matches the expected result type. 117 if (expectedResult != typeof(void) && 118 !TypeUtils.AreReferenceAssignable(expectedResult, body.Type)) { 119 120 // 121 // Blame the last person that handled the result: assume it's 122 // the dynamic object (if any), otherwise blame the language. 123 // 124 if (target.Value is IDynamicMetaObjectProvider) { 125 throw Error.DynamicObjectResultNotAssignable(body.Type, target.Value.GetType(), this, expectedResult); 126 } else { 127 throw Error.DynamicBinderResultNotAssignable(body.Type, this, expectedResult); 128 } 129 } 130 131 // if the target is IDO, standard binders ask it to bind the rule so we may have a target-specific binding. 132 // it makes sense to restrict on the target's type in such cases. 133 // ideally IDO metaobjects should do this, but they often miss that type of "this" is significant. 134 if (IsStandardBinder && args[0] as IDynamicMetaObjectProvider != null) { 135 if (restrictions == BindingRestrictions.Empty) { 136 throw Error.DynamicBindingNeedsRestrictions(target.Value.GetType(), this); 137 } 138 } 139 140 restrictions = AddRemoteObjectRestrictions(restrictions, args, parameters); 141 142 // Add the return 143 if (body.NodeType != ExpressionType.Goto) { 144 body = Expression.Return(returnLabel, body); 145 } 146 147 // Finally, add restrictions 148 if (restrictions != BindingRestrictions.Empty) { 149 body = Expression.IfThen(restrictions.ToExpression(), body); 150 } 151 152 return body; 153 } 154 CreateArgumentMetaObjects(object[] args, ReadOnlyCollection<ParameterExpression> parameters)155 private static DynamicMetaObject[] CreateArgumentMetaObjects(object[] args, ReadOnlyCollection<ParameterExpression> parameters) { 156 DynamicMetaObject[] mos; 157 if (args.Length != 1) { 158 mos = new DynamicMetaObject[args.Length - 1]; 159 for (int i = 1; i < args.Length; i++) { 160 mos[i - 1] = DynamicMetaObject.Create(args[i], parameters[i]); 161 } 162 } else { 163 mos = DynamicMetaObject.EmptyMetaObjects; 164 } 165 return mos; 166 } 167 AddRemoteObjectRestrictions(BindingRestrictions restrictions, object[] args, ReadOnlyCollection<ParameterExpression> parameters)168 private static BindingRestrictions AddRemoteObjectRestrictions(BindingRestrictions restrictions, object[] args, ReadOnlyCollection<ParameterExpression> parameters) { 169 #if !SILVERLIGHT 170 171 for (int i = 0; i < parameters.Count; i++) { 172 var expr = parameters[i]; 173 var value = args[i] as MarshalByRefObject; 174 175 // special case for MBR objects. 176 // when MBR objects are remoted they can have different conversion behavior 177 // so bindings created for local and remote objects should not be mixed. 178 if (value != null && !IsComObject(value)) { 179 BindingRestrictions remotedRestriction; 180 if (RemotingServices.IsObjectOutOfAppDomain(value)) { 181 remotedRestriction = BindingRestrictions.GetExpressionRestriction( 182 Expression.AndAlso( 183 Expression.NotEqual(expr, Expression.Constant(null)), 184 Expression.Call( 185 typeof(RemotingServices).GetMethod("IsObjectOutOfAppDomain"), 186 expr 187 ) 188 ) 189 ); 190 } else { 191 remotedRestriction = BindingRestrictions.GetExpressionRestriction( 192 Expression.AndAlso( 193 Expression.NotEqual(expr, Expression.Constant(null)), 194 Expression.Not( 195 Expression.Call( 196 typeof(RemotingServices).GetMethod("IsObjectOutOfAppDomain"), 197 expr 198 ) 199 ) 200 ) 201 ); 202 } 203 restrictions = restrictions.Merge(remotedRestriction); 204 } 205 } 206 207 #endif 208 return restrictions; 209 } 210 211 /// <summary> 212 /// When overridden in the derived class, performs the binding of the dynamic operation. 213 /// </summary> 214 /// <param name="target">The target of the dynamic operation.</param> 215 /// <param name="args">An array of arguments of the dynamic operation.</param> 216 /// <returns>The <see cref="DynamicMetaObject"/> representing the result of the binding.</returns> Bind(DynamicMetaObject target, DynamicMetaObject[] args)217 public abstract DynamicMetaObject Bind(DynamicMetaObject target, DynamicMetaObject[] args); 218 219 /// <summary> 220 /// Gets an expression that will cause the binding to be updated. It 221 /// indicates that the expression's binding is no longer valid. 222 /// This is typically used when the "version" of a dynamic object has 223 /// changed. 224 /// </summary> 225 /// <param name="type">The <see cref="Expression.Type">Type</see> property of the resulting expression; any type is allowed.</param> 226 /// <returns>The update expression.</returns> 227 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")] GetUpdateExpression(Type type)228 public Expression GetUpdateExpression(Type type) { 229 return Expression.Goto(CallSiteBinder.UpdateLabel, type); 230 } 231 232 /// <summary> 233 /// Defers the binding of the operation until later time when the runtime values of all dynamic operation arguments have been computed. 234 /// </summary> 235 /// <param name="target">The target of the dynamic operation.</param> 236 /// <param name="args">An array of arguments of the dynamic operation.</param> 237 /// <returns>The <see cref="DynamicMetaObject"/> representing the result of the binding.</returns> Defer(DynamicMetaObject target, params DynamicMetaObject[] args)238 public DynamicMetaObject Defer(DynamicMetaObject target, params DynamicMetaObject[] args) { 239 ContractUtils.RequiresNotNull(target, "target"); 240 241 if (args == null) { 242 return MakeDeferred(target.Restrictions, target); 243 } else { 244 return MakeDeferred( 245 target.Restrictions.Merge(BindingRestrictions.Combine(args)), 246 args.AddFirst(target) 247 ); 248 } 249 } 250 251 /// <summary> 252 /// Defers the binding of the operation until later time when the runtime values of all dynamic operation arguments have been computed. 253 /// </summary> 254 /// <param name="args">An array of arguments of the dynamic operation.</param> 255 /// <returns>The <see cref="DynamicMetaObject"/> representing the result of the binding.</returns> Defer(params DynamicMetaObject[] args)256 public DynamicMetaObject Defer(params DynamicMetaObject[] args) { 257 return MakeDeferred(BindingRestrictions.Combine(args), args); 258 } 259 MakeDeferred(BindingRestrictions rs, params DynamicMetaObject[] args)260 private DynamicMetaObject MakeDeferred(BindingRestrictions rs, params DynamicMetaObject[] args) { 261 var exprs = DynamicMetaObject.GetExpressions(args); 262 263 Type delegateType = DelegateHelpers.MakeDeferredSiteDelegate(args, ReturnType); 264 265 // Because we know the arguments match the delegate type (we just created the argument types) 266 // we go directly to DynamicExpression.Make to avoid a bunch of unnecessary argument validation 267 return new DynamicMetaObject( 268 DynamicExpression.Make(ReturnType, delegateType, this, new TrueReadOnlyCollection<Expression>(exprs)), 269 rs 270 ); 271 } 272 273 #endregion 274 275 // used to detect standard MetaObjectBinders. 276 internal virtual bool IsStandardBinder { 277 get { 278 return false; 279 } 280 } 281 282 #if !SILVERLIGHT 283 private static readonly Type ComObjectType = typeof(object).Assembly.GetType("System.__ComObject"); IsComObject(object obj)284 private static bool IsComObject(object obj) { 285 // we can't use System.Runtime.InteropServices.Marshal.IsComObject(obj) since it doesn't work in partial trust 286 return obj != null && ComObjectType.IsAssignableFrom(obj.GetType()); 287 } 288 #endif 289 290 } 291 } 292