| File: System\Dynamic\DynamicMetaObjectBinder.cs | Web Access |
| Project: src\runtime\src\libraries\System.Linq.Expressions\src\System.Linq.Expressions.csproj (System.Linq.Expressions) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.ObjectModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Dynamic.Utils; using System.Linq.Expressions; using System.Runtime.CompilerServices; using DelegateHelpers = System.Linq.Expressions.Compiler.DelegateHelpers; namespace System.Dynamic { /// <summary> /// The dynamic call site binder that participates in the <see cref="DynamicMetaObject"/> binding protocol. /// </summary> /// <remarks> /// The <see cref="CallSiteBinder"/> performs the binding of the dynamic operation using the runtime values /// as input. On the other hand, the <see cref="DynamicMetaObjectBinder"/> participates in the <see cref="DynamicMetaObject"/> /// binding protocol. /// </remarks> [RequiresDynamicCode(Expression.CallSiteRequiresDynamicCode)] public abstract class DynamicMetaObjectBinder : CallSiteBinder { /// <summary> /// Initializes a new instance of the <see cref="DynamicMetaObjectBinder"/> class. /// </summary> protected DynamicMetaObjectBinder() { } /// <summary> /// The result type of the operation. /// </summary> public virtual Type ReturnType => typeof(object); /// <summary> /// Performs the runtime binding of the dynamic operation on a set of arguments. /// </summary> /// <param name="args">An array of arguments to the dynamic operation.</param> /// <param name="parameters">The array of <see cref="ParameterExpression"/> instances that represent the parameters of the call site in the binding process.</param> /// <param name="returnLabel">A LabelTarget used to return the result of the dynamic binding.</param> /// <returns> /// An Expression that performs tests on the dynamic operation arguments, and /// performs the dynamic operation if the tests are valid. If the tests fail on /// subsequent occurrences of the dynamic operation, Bind will be called again /// to produce a new <see cref="Expression"/> for the new argument types. /// </returns> public sealed override Expression Bind(object[] args, ReadOnlyCollection<ParameterExpression> parameters, LabelTarget returnLabel) { ArgumentNullException.ThrowIfNull(args); ArgumentNullException.ThrowIfNull(parameters); ArgumentNullException.ThrowIfNull(returnLabel); ArgumentOutOfRangeException.ThrowIfZero(args.Length); ArgumentOutOfRangeException.ThrowIfZero(parameters.Count); ArgumentOutOfRangeException.ThrowIfNotEqual(args.Length, parameters.Count); // 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] is IDynamicMetaObjectProvider) { 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; } private static DynamicMetaObject[] CreateArgumentMetaObjects(object[] args, ReadOnlyCollection<ParameterExpression> parameters) { DynamicMetaObject[] mos; if (args.Length != 1) { mos = new DynamicMetaObject[args.Length - 1]; for (int i = 1; i < args.Length; i++) { mos[i - 1] = DynamicMetaObject.Create(args[i], parameters[i]); } } else { mos = DynamicMetaObject.EmptyMetaObjects; } return mos; } /// <summary> /// When overridden in the derived class, performs the binding of the dynamic operation. /// </summary> /// <param name="target">The target of the dynamic operation.</param> /// <param name="args">An array of arguments of the dynamic operation.</param> /// <returns>The <see cref="DynamicMetaObject"/> representing the result of the binding.</returns> public abstract DynamicMetaObject Bind(DynamicMetaObject target, DynamicMetaObject[] args); /// <summary> /// Gets an expression that will cause the binding to be updated. It /// indicates that the expression's binding is no longer valid. /// This is typically used when the "version" of a dynamic object has /// changed. /// </summary> /// <param name="type">The <see cref="Expression.Type">Type</see> property of the resulting expression; any type is allowed.</param> /// <returns>The update expression.</returns> public Expression GetUpdateExpression(Type type) { return Expression.Goto(CallSiteBinder.UpdateLabel, type); } /// <summary> /// Defers the binding of the operation until later time when the runtime values of all dynamic operation arguments have been computed. /// </summary> /// <param name="target">The target of the dynamic operation.</param> /// <param name="args">An array of arguments of the dynamic operation.</param> /// <returns>The <see cref="DynamicMetaObject"/> representing the result of the binding.</returns> public DynamicMetaObject Defer(DynamicMetaObject target, params DynamicMetaObject[]? args) { ArgumentNullException.ThrowIfNull(target); if (args == null) { return MakeDeferred(target.Restrictions, target); } else { return MakeDeferred( target.Restrictions.Merge(BindingRestrictions.Combine(args)), args.AddFirst(target) ); } } /// <summary> /// Defers the binding of the operation until later time when the runtime values of all dynamic operation arguments have been computed. /// </summary> /// <param name="args">An array of arguments of the dynamic operation.</param> /// <returns>The <see cref="DynamicMetaObject"/> representing the result of the binding.</returns> public DynamicMetaObject Defer(params DynamicMetaObject[] args) { return MakeDeferred(BindingRestrictions.Combine(args), args); } private DynamicMetaObject MakeDeferred(BindingRestrictions rs, params DynamicMetaObject[] args) { var exprs = DynamicMetaObject.GetExpressions(args); Type delegateType = DelegateHelpers.MakeDeferredSiteDelegate(args, ReturnType); // Because we know the arguments match the delegate type (we just created the argument types) // we go directly to DynamicExpression.Make to avoid a bunch of unnecessary argument validation return new DynamicMetaObject( DynamicExpression.Make(ReturnType, delegateType, this, new TrueReadOnlyCollection<Expression>(exprs)), rs ); } /// <summary> /// Returns <c>true</c> for standard <see cref="DynamicMetaObjectBinder"/>s; otherwise, <c>false</c>. /// </summary> internal virtual bool IsStandardBinder => false; } }