1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4 
5 using System.Collections.Generic;
6 using System.Diagnostics;
7 using System.Dynamic.Utils;
8 using System.Linq.Expressions;
9 
10 namespace System.Dynamic
11 {
12     /// <summary>
13     /// Represents the dynamic binding and a binding logic of an object participating in the dynamic binding.
14     /// </summary>
15     public class DynamicMetaObject
16     {
17         /// <summary>
18         /// Represents an empty array of type <see cref="DynamicMetaObject"/>. This field is read-only.
19         /// </summary>
20         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2105:ArrayFieldsShouldNotBeReadOnly")]
21         public static readonly DynamicMetaObject[] EmptyMetaObjects = Array.Empty<DynamicMetaObject>();
22 
23         /// <summary>
24         /// Initializes a new instance of the <see cref="DynamicMetaObject"/> class.
25         /// </summary>
26         /// <param name="expression">The expression representing this <see cref="DynamicMetaObject"/> during the dynamic binding process.</param>
27         /// <param name="restrictions">The set of binding restrictions under which the binding is valid.</param>
DynamicMetaObject(Expression expression, BindingRestrictions restrictions)28         public DynamicMetaObject(Expression expression, BindingRestrictions restrictions)
29         {
30             ContractUtils.RequiresNotNull(expression, nameof(expression));
31             ContractUtils.RequiresNotNull(restrictions, nameof(restrictions));
32 
33             Expression = expression;
34             Restrictions = restrictions;
35         }
36 
37         /// <summary>
38         /// Initializes a new instance of the <see cref="DynamicMetaObject"/> class.
39         /// </summary>
40         /// <param name="expression">The expression representing this <see cref="DynamicMetaObject"/> during the dynamic binding process.</param>
41         /// <param name="restrictions">The set of binding restrictions under which the binding is valid.</param>
42         /// <param name="value">The runtime value represented by the <see cref="DynamicMetaObject"/>.</param>
DynamicMetaObject(Expression expression, BindingRestrictions restrictions, object value)43         public DynamicMetaObject(Expression expression, BindingRestrictions restrictions, object value)
44             : this(expression, restrictions)
45         {
46             Value = value;
47             HasValue = true;
48         }
49 
50         /// <summary>
51         /// The expression representing the <see cref="DynamicMetaObject"/> during the dynamic binding process.
52         /// </summary>
53         public Expression Expression { get; }
54 
55         /// <summary>
56         /// The set of binding restrictions under which the binding is valid.
57         /// </summary>
58         public BindingRestrictions Restrictions { get; }
59 
60         /// <summary>
61         /// The runtime value represented by this <see cref="DynamicMetaObject"/>.
62         /// </summary>
63         public object Value { get; }
64 
65         /// <summary>
66         /// Gets a value indicating whether the <see cref="DynamicMetaObject"/> has the runtime value.
67         /// </summary>
68         public bool HasValue { get; }
69 
70         /// <summary>
71         /// Gets the <see cref="Type"/> of the runtime value or null if the <see cref="DynamicMetaObject"/> has no value associated with it.
72         /// </summary>
73         public Type RuntimeType
74         {
75             get
76             {
77                 if (HasValue)
78                 {
79                     Type ct = Expression.Type;
80                     // valuetype at compile time, type cannot change.
81                     if (ct.IsValueType)
82                     {
83                         return ct;
84                     }
85 
86                     return Value?.GetType();
87                 }
88                 else
89                 {
90                     return null;
91                 }
92             }
93         }
94 
95         /// <summary>
96         /// Gets the limit type of the <see cref="DynamicMetaObject"/>.
97         /// </summary>
98         /// <remarks>Represents the most specific type known about the object represented by the <see cref="DynamicMetaObject"/>. <see cref="RuntimeType"/> if runtime value is available, a type of the <see cref="Expression"/> otherwise.</remarks>
99         public Type LimitType => RuntimeType ?? Expression.Type;
100 
101         /// <summary>
102         /// Performs the binding of the dynamic conversion operation.
103         /// </summary>
104         /// <param name="binder">An instance of the <see cref="ConvertBinder"/> that represents the details of the dynamic operation.</param>
105         /// <returns>The new <see cref="DynamicMetaObject"/> representing the result of the binding.</returns>
BindConvert(ConvertBinder binder)106         public virtual DynamicMetaObject BindConvert(ConvertBinder binder)
107         {
108             ContractUtils.RequiresNotNull(binder, nameof(binder));
109             return binder.FallbackConvert(this);
110         }
111 
112         /// <summary>
113         /// Performs the binding of the dynamic get member operation.
114         /// </summary>
115         /// <param name="binder">An instance of the <see cref="GetMemberBinder"/> that represents the details of the dynamic operation.</param>
116         /// <returns>The new <see cref="DynamicMetaObject"/> representing the result of the binding.</returns>
BindGetMember(GetMemberBinder binder)117         public virtual DynamicMetaObject BindGetMember(GetMemberBinder binder)
118         {
119             ContractUtils.RequiresNotNull(binder, nameof(binder));
120             return binder.FallbackGetMember(this);
121         }
122 
123         /// <summary>
124         /// Performs the binding of the dynamic set member operation.
125         /// </summary>
126         /// <param name="binder">An instance of the <see cref="SetMemberBinder"/> that represents the details of the dynamic operation.</param>
127         /// <param name="value">The <see cref="DynamicMetaObject"/> representing the value for the set member operation.</param>
128         /// <returns>The new <see cref="DynamicMetaObject"/> representing the result of the binding.</returns>
BindSetMember(SetMemberBinder binder, DynamicMetaObject value)129         public virtual DynamicMetaObject BindSetMember(SetMemberBinder binder, DynamicMetaObject value)
130         {
131             ContractUtils.RequiresNotNull(binder, nameof(binder));
132             return binder.FallbackSetMember(this, value);
133         }
134 
135         /// <summary>
136         /// Performs the binding of the dynamic delete member operation.
137         /// </summary>
138         /// <param name="binder">An instance of the <see cref="DeleteMemberBinder"/> that represents the details of the dynamic operation.</param>
139         /// <returns>The new <see cref="DynamicMetaObject"/> representing the result of the binding.</returns>
BindDeleteMember(DeleteMemberBinder binder)140         public virtual DynamicMetaObject BindDeleteMember(DeleteMemberBinder binder)
141         {
142             ContractUtils.RequiresNotNull(binder, nameof(binder));
143             return binder.FallbackDeleteMember(this);
144         }
145 
146         /// <summary>
147         /// Performs the binding of the dynamic get index operation.
148         /// </summary>
149         /// <param name="binder">An instance of the <see cref="GetIndexBinder"/> that represents the details of the dynamic operation.</param>
150         /// <param name="indexes">An array of <see cref="DynamicMetaObject"/> instances - indexes for the get index operation.</param>
151         /// <returns>The new <see cref="DynamicMetaObject"/> representing the result of the binding.</returns>
BindGetIndex(GetIndexBinder binder, DynamicMetaObject[] indexes)152         public virtual DynamicMetaObject BindGetIndex(GetIndexBinder binder, DynamicMetaObject[] indexes)
153         {
154             ContractUtils.RequiresNotNull(binder, nameof(binder));
155             return binder.FallbackGetIndex(this, indexes);
156         }
157 
158         /// <summary>
159         /// Performs the binding of the dynamic set index operation.
160         /// </summary>
161         /// <param name="binder">An instance of the <see cref="SetIndexBinder"/> that represents the details of the dynamic operation.</param>
162         /// <param name="indexes">An array of <see cref="DynamicMetaObject"/> instances - indexes for the set index operation.</param>
163         /// <param name="value">The <see cref="DynamicMetaObject"/> representing the value for the set index operation.</param>
164         /// <returns>The new <see cref="DynamicMetaObject"/> representing the result of the binding.</returns>
BindSetIndex(SetIndexBinder binder, DynamicMetaObject[] indexes, DynamicMetaObject value)165         public virtual DynamicMetaObject BindSetIndex(SetIndexBinder binder, DynamicMetaObject[] indexes, DynamicMetaObject value)
166         {
167             ContractUtils.RequiresNotNull(binder, nameof(binder));
168             return binder.FallbackSetIndex(this, indexes, value);
169         }
170 
171         /// <summary>
172         /// Performs the binding of the dynamic delete index operation.
173         /// </summary>
174         /// <param name="binder">An instance of the <see cref="DeleteIndexBinder"/> that represents the details of the dynamic operation.</param>
175         /// <param name="indexes">An array of <see cref="DynamicMetaObject"/> instances - indexes for the delete index operation.</param>
176         /// <returns>The new <see cref="DynamicMetaObject"/> representing the result of the binding.</returns>
BindDeleteIndex(DeleteIndexBinder binder, DynamicMetaObject[] indexes)177         public virtual DynamicMetaObject BindDeleteIndex(DeleteIndexBinder binder, DynamicMetaObject[] indexes)
178         {
179             ContractUtils.RequiresNotNull(binder, nameof(binder));
180             return binder.FallbackDeleteIndex(this, indexes);
181         }
182 
183         /// <summary>
184         /// Performs the binding of the dynamic invoke member operation.
185         /// </summary>
186         /// <param name="binder">An instance of the <see cref="InvokeMemberBinder"/> that represents the details of the dynamic operation.</param>
187         /// <param name="args">An array of <see cref="DynamicMetaObject"/> instances - arguments to the invoke member operation.</param>
188         /// <returns>The new <see cref="DynamicMetaObject"/> representing the result of the binding.</returns>
BindInvokeMember(InvokeMemberBinder binder, DynamicMetaObject[] args)189         public virtual DynamicMetaObject BindInvokeMember(InvokeMemberBinder binder, DynamicMetaObject[] args)
190         {
191             ContractUtils.RequiresNotNull(binder, nameof(binder));
192             return binder.FallbackInvokeMember(this, args);
193         }
194 
195         /// <summary>
196         /// Performs the binding of the dynamic invoke operation.
197         /// </summary>
198         /// <param name="binder">An instance of the <see cref="InvokeBinder"/> that represents the details of the dynamic operation.</param>
199         /// <param name="args">An array of <see cref="DynamicMetaObject"/> instances - arguments to the invoke operation.</param>
200         /// <returns>The new <see cref="DynamicMetaObject"/> representing the result of the binding.</returns>
BindInvoke(InvokeBinder binder, DynamicMetaObject[] args)201         public virtual DynamicMetaObject BindInvoke(InvokeBinder binder, DynamicMetaObject[] args)
202         {
203             ContractUtils.RequiresNotNull(binder, nameof(binder));
204             return binder.FallbackInvoke(this, args);
205         }
206 
207         /// <summary>
208         /// Performs the binding of the dynamic create instance operation.
209         /// </summary>
210         /// <param name="binder">An instance of the <see cref="CreateInstanceBinder"/> that represents the details of the dynamic operation.</param>
211         /// <param name="args">An array of <see cref="DynamicMetaObject"/> instances - arguments to the create instance operation.</param>
212         /// <returns>The new <see cref="DynamicMetaObject"/> representing the result of the binding.</returns>
BindCreateInstance(CreateInstanceBinder binder, DynamicMetaObject[] args)213         public virtual DynamicMetaObject BindCreateInstance(CreateInstanceBinder binder, DynamicMetaObject[] args)
214         {
215             ContractUtils.RequiresNotNull(binder, nameof(binder));
216             return binder.FallbackCreateInstance(this, args);
217         }
218 
219         /// <summary>
220         /// Performs the binding of the dynamic unary operation.
221         /// </summary>
222         /// <param name="binder">An instance of the <see cref="UnaryOperationBinder"/> that represents the details of the dynamic operation.</param>
223         /// <returns>The new <see cref="DynamicMetaObject"/> representing the result of the binding.</returns>
BindUnaryOperation(UnaryOperationBinder binder)224         public virtual DynamicMetaObject BindUnaryOperation(UnaryOperationBinder binder)
225         {
226             ContractUtils.RequiresNotNull(binder, nameof(binder));
227             return binder.FallbackUnaryOperation(this);
228         }
229 
230         /// <summary>
231         /// Performs the binding of the dynamic binary operation.
232         /// </summary>
233         /// <param name="binder">An instance of the <see cref="BinaryOperationBinder"/> that represents the details of the dynamic operation.</param>
234         /// <param name="arg">An instance of the <see cref="DynamicMetaObject"/> representing the right hand side of the binary operation.</param>
235         /// <returns>The new <see cref="DynamicMetaObject"/> representing the result of the binding.</returns>
BindBinaryOperation(BinaryOperationBinder binder, DynamicMetaObject arg)236         public virtual DynamicMetaObject BindBinaryOperation(BinaryOperationBinder binder, DynamicMetaObject arg)
237         {
238             ContractUtils.RequiresNotNull(binder, nameof(binder));
239             return binder.FallbackBinaryOperation(this, arg);
240         }
241 
242         /// <summary>
243         /// Returns the enumeration of all dynamic member names.
244         /// </summary>
245         /// <returns>The list of dynamic member names.</returns>
246         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")]
GetDynamicMemberNames()247         public virtual IEnumerable<string> GetDynamicMemberNames() => Array.Empty<string>();
248 
249         /// <summary>
250         /// Returns the list of expressions represented by the <see cref="DynamicMetaObject"/> instances.
251         /// </summary>
252         /// <param name="objects">An array of <see cref="DynamicMetaObject"/> instances to extract expressions from.</param>
253         /// <returns>The array of expressions.</returns>
GetExpressions(DynamicMetaObject[] objects)254         internal static Expression[] GetExpressions(DynamicMetaObject[] objects)
255         {
256             ContractUtils.RequiresNotNull(objects, nameof(objects));
257 
258             Expression[] res = new Expression[objects.Length];
259             for (int i = 0; i < objects.Length; i++)
260             {
261                 DynamicMetaObject mo = objects[i];
262                 ContractUtils.RequiresNotNull(mo, nameof(objects));
263                 Expression expr = mo.Expression;
264                 Debug.Assert(expr != null, "Unexpected null expression; ctor should have caught this.");
265                 res[i] = expr;
266             }
267 
268             return res;
269         }
270 
271         /// <summary>
272         /// Creates a meta-object for the specified object.
273         /// </summary>
274         /// <param name="value">The object to get a meta-object for.</param>
275         /// <param name="expression">The expression representing this <see cref="DynamicMetaObject"/> during the dynamic binding process.</param>
276         /// <returns>
277         /// If the given object implements <see cref="IDynamicMetaObjectProvider"/> and is not a remote object from outside the current AppDomain,
278         /// returns the object's specific meta-object returned by <see cref="IDynamicMetaObjectProvider.GetMetaObject"/>. Otherwise a plain new meta-object
279         /// with no restrictions is created and returned.
280         /// </returns>
Create(object value, Expression expression)281         public static DynamicMetaObject Create(object value, Expression expression)
282         {
283             ContractUtils.RequiresNotNull(expression, nameof(expression));
284 
285             IDynamicMetaObjectProvider ido = value as IDynamicMetaObjectProvider;
286             if (ido != null)
287             {
288                 var idoMetaObject = ido.GetMetaObject(expression);
289 
290                 if (idoMetaObject == null ||
291                     !idoMetaObject.HasValue ||
292                     idoMetaObject.Value == null ||
293                     (object)idoMetaObject.Expression != (object)expression)
294                 {
295                     throw System.Linq.Expressions.Error.InvalidMetaObjectCreated(ido.GetType());
296                 }
297 
298                 return idoMetaObject;
299             }
300             else
301             {
302                 return new DynamicMetaObject(expression, BindingRestrictions.Empty, value);
303             }
304         }
305     }
306 }
307