1 /* ****************************************************************************
3 * Copyright (c) Microsoft Corporation.
5 * This source code is subject to terms and conditions of the Microsoft Public License. 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 Microsoft Public License, 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 Microsoft Public License.
11 * You must not remove this notice, or any other, from this software.
14 * ***************************************************************************/
15 using System; using Microsoft;
18 using System.Collections.ObjectModel;
19 using System.Diagnostics;
21 using System.Dynamic.Utils;
22 using System.Linq.Expressions;
23 using System.Linq.Expressions.Compiler;
25 using Microsoft.Scripting.Utils;
26 using Microsoft.Linq.Expressions;
27 using Microsoft.Linq.Expressions.Compiler;
29 using System.Runtime.CompilerServices;
31 using Microsoft.Runtime.CompilerServices;
34 using System.Runtime.Remoting;
37 namespace System.Dynamic {
39 namespace Microsoft.Scripting {
42 /// The dynamic call site binder that participates in the <see cref="DynamicMetaObject"/> binding protocol.
45 /// The <see cref="CallSiteBinder"/> performs the binding of the dynamic operation using the runtime values
46 /// as input. On the other hand, the <see cref="DynamicMetaObjectBinder"/> participates in the <see cref="DynamicMetaObject"/>
49 public abstract class DynamicMetaObjectBinder : CallSiteBinder {
54 /// Initializes a new instance of the <see cref="DynamicMetaObjectBinder"/> class.
56 protected DynamicMetaObjectBinder() {
60 /// The result type of the operation.
62 public virtual Type ReturnType {
63 get { return typeof(object); }
67 /// Gets the value indicating if we should validate the result of the binding.
69 protected virtual bool ValidateBindingResult {
74 /// Performs the runtime binding of the dynamic operation on a set of arguments.
76 /// <param name="args">An array of arguments to the dynamic operation.</param>
77 /// <param name="parameters">The array of <see cref="ParameterExpression"/> instances that represent the parameters of the call site in the binding process.</param>
78 /// <param name="returnLabel">A LabelTarget used to return the result of the dynamic binding.</param>
80 /// An Expression that performs tests on the dynamic operation arguments, and
81 /// performs the dynamic operation if hte tests are valid. If the tests fail on
82 /// subsequent occurrences of the dynamic operation, Bind will be called again
83 /// to produce a new <see cref="Expression"/> for the new argument types.
85 public sealed override Expression Bind(object[] args, ReadOnlyCollection<ParameterExpression> parameters, LabelTarget returnLabel) {
86 ContractUtils.RequiresNotNull(args, "args");
87 ContractUtils.RequiresNotNull(parameters, "parameters");
88 ContractUtils.RequiresNotNull(returnLabel, "returnLabel");
89 if (args.Length == 0) {
90 throw Error.OutOfRange("args.Length", 1);
92 if (parameters.Count == 0) {
93 throw Error.OutOfRange("parameters.Count", 1);
95 if (args.Length != parameters.Count) {
96 throw new ArgumentOutOfRangeException("args");
99 // Ensure that the binder's ReturnType matches CallSite's return
100 // type. We do this so meta objects and language binders can
101 // compose trees together without needing to insert converts.
103 // For now, we need to allow binders to opt out of this check.
105 if (ValidateBindingResult) {
106 expectedResult = ReturnType;
108 if (returnLabel.Type != typeof(void) &&
109 !TypeUtils.AreReferenceAssignable(returnLabel.Type, expectedResult)) {
110 throw Error.BinderNotCompatibleWithCallSite(expectedResult, this, returnLabel.Type);
113 // We have to at least make sure it works with the CallSite's
114 // type to build the return.
115 expectedResult = returnLabel.Type;
118 DynamicMetaObject target = DynamicMetaObject.Create(args[0], parameters[0]);
119 DynamicMetaObject[] metaArgs = CreateArgumentMetaObjects(args, parameters);
121 DynamicMetaObject binding = Bind(target, metaArgs);
123 if (binding == null) {
124 throw Error.BindingCannotBeNull();
127 Expression body = binding.Expression;
128 BindingRestrictions restrictions = binding.Restrictions;
130 // Ensure the result matches the expected result type.
131 if (expectedResult != typeof(void) &&
132 !TypeUtils.AreReferenceAssignable(expectedResult, body.Type)) {
135 // Blame the last person that handled the result: assume it's
136 // the dynamic object (if any), otherwise blame the language.
138 if (target.Value is IDynamicMetaObjectProvider) {
139 throw Error.DynamicObjectResultNotAssignable(body.Type, target.Value.GetType(), this, expectedResult);
141 throw Error.DynamicBinderResultNotAssignable(body.Type, this, expectedResult);
145 // if the target is IDO, standard binders ask it to bind the rule so we may have a target-specific binding.
146 // it makes sense to restrict on the target's type in such cases.
147 // ideally IDO metaobjects should do this, but they often miss that type of "this" is significant.
148 if (IsStandardBinder && args[0] as IDynamicMetaObjectProvider != null) {
149 if (restrictions == BindingRestrictions.Empty) {
150 throw Error.DynamicBindingNeedsRestrictions(target.Value.GetType(), this);
154 restrictions = AddRemoteObjectRestrictions(restrictions, args, parameters);
157 if (body.NodeType != ExpressionType.Goto) {
158 body = Expression.Return(returnLabel, body);
161 // Finally, add restrictions
162 if (restrictions != BindingRestrictions.Empty) {
163 body = Expression.IfThen(restrictions.ToExpression(), body);
169 private static DynamicMetaObject[] CreateArgumentMetaObjects(object[] args, ReadOnlyCollection<ParameterExpression> parameters) {
170 DynamicMetaObject[] mos;
171 if (args.Length != 1) {
172 mos = new DynamicMetaObject[args.Length - 1];
173 for (int i = 1; i < args.Length; i++) {
174 mos[i - 1] = DynamicMetaObject.Create(args[i], parameters[i]);
177 mos = DynamicMetaObject.EmptyMetaObjects;
182 private static BindingRestrictions AddRemoteObjectRestrictions(BindingRestrictions restrictions, object[] args, ReadOnlyCollection<ParameterExpression> parameters) {
185 for (int i = 0; i < parameters.Count; i++) {
186 var expr = parameters[i];
187 var value = args[i] as MarshalByRefObject;
189 // special case for MBR objects.
190 // when MBR objects are remoted they can have different conversion behavior
191 // so bindings created for local and remote objects should not be mixed.
192 if (value != null && !IsComObject(value)) {
193 BindingRestrictions remotedRestriction;
194 if (RemotingServices.IsObjectOutOfAppDomain(value)) {
195 remotedRestriction = BindingRestrictions.GetExpressionRestriction(
197 Expression.NotEqual(expr, Expression.Constant(null)),
199 typeof(RemotingServices).GetMethod("IsObjectOutOfAppDomain"),
205 remotedRestriction = BindingRestrictions.GetExpressionRestriction(
207 Expression.NotEqual(expr, Expression.Constant(null)),
210 typeof(RemotingServices).GetMethod("IsObjectOutOfAppDomain"),
217 restrictions = restrictions.Merge(remotedRestriction);
226 /// When overridden in the derived class, performs the binding of the dynamic operation.
228 /// <param name="target">The target of the dynamic operation.</param>
229 /// <param name="args">An array of arguments of the dynamic operation.</param>
230 /// <returns>The <see cref="DynamicMetaObject"/> representing the result of the binding.</returns>
231 public abstract DynamicMetaObject Bind(DynamicMetaObject target, DynamicMetaObject[] args);
234 /// Gets an expression that will cause the binding to be updated. It
235 /// indicates that the expression's binding is no longer valid.
236 /// This is typically used when the "version" of a dynamic object has
239 /// <param name="type">The <see cref="Expression.Type">Type</see> property of the resulting expression; any type is allowed.</param>
240 /// <returns>The update expression.</returns>
241 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
242 public Expression GetUpdateExpression(Type type) {
243 return Expression.Goto(CallSiteBinder.UpdateLabel, type);
247 /// Defers the binding of the operation until later time when the runtime values of all dynamic operation arguments have been computed.
249 /// <param name="target">The target of the dynamic operation.</param>
250 /// <param name="args">An array of arguments of the dynamic operation.</param>
251 /// <returns>The <see cref="DynamicMetaObject"/> representing the result of the binding.</returns>
252 public DynamicMetaObject Defer(DynamicMetaObject target, params DynamicMetaObject[] args) {
253 ContractUtils.RequiresNotNull(target, "target");
256 return MakeDeferred(target.Restrictions, target);
259 target.Restrictions.Merge(BindingRestrictions.Combine(args)),
260 args.AddFirst(target)
266 /// Defers the binding of the operation until later time when the runtime values of all dynamic operation arguments have been computed.
268 /// <param name="args">An array of arguments of the dynamic operation.</param>
269 /// <returns>The <see cref="DynamicMetaObject"/> representing the result of the binding.</returns>
270 public DynamicMetaObject Defer(params DynamicMetaObject[] args) {
271 return MakeDeferred(BindingRestrictions.Combine(args), args);
274 private DynamicMetaObject MakeDeferred(BindingRestrictions rs, params DynamicMetaObject[] args) {
275 var exprs = DynamicMetaObject.GetExpressions(args);
277 Type delegateType = DelegateHelpers.MakeDeferredSiteDelegate(args, ReturnType);
279 // Because we know the arguments match the delegate type (we just created the argument types)
280 // we go directly to DynamicExpression.Make to avoid a bunch of unnecessary argument validation
281 return new DynamicMetaObject(
282 DynamicExpression.Make(ReturnType, delegateType, this, new TrueReadOnlyCollection<Expression>(exprs)),
289 // used to detect standard MetaObjectBinders.
290 internal virtual bool IsStandardBinder {
297 private static readonly Type ComObjectType = typeof(object).Assembly.GetType("System.__ComObject");
298 private static bool IsComObject(object obj) {
299 // we can't use System.Runtime.InteropServices.Marshal.IsComObject(obj) since it doesn't work in partial trust
300 return obj != null && ComObjectType.IsAssignableFrom(obj.GetType());