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