2009-07-11 Michael Barker <mike@middlesoft.co.uk>
[mono.git] / mcs / class / dlr / Runtime / Microsoft.Scripting.Core / Actions / DynamicObject.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.Diagnostics;
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
28 #if CODEPLEX_40
29 namespace System.Dynamic {
30 #else
31 namespace Microsoft.Scripting {
32 #endif
33     /// <summary>
34     /// Provides a simple class that can be inherited from to create an object with dynamic behavior
35     /// at runtime.  Subclasses can override the various binder methods (GetMember, SetMember, Call, etc...)
36     /// to provide custom behavior that will be invoked at runtime.  
37     /// 
38     /// If a method is not overridden then the DynamicObject does not directly support that behavior and 
39     /// the call site will determine how the binding should be performed.
40     /// </summary>
41     public class DynamicObject : IDynamicMetaObjectProvider {
42
43         /// <summary>
44         /// Enables derived types to create a new instance of DynamicObject.  DynamicObject instances cannot be
45         /// directly instantiated because they have no implementation of dynamic behavior.
46         /// </summary>
47         protected DynamicObject() {
48         }
49
50         #region Public Virtual APIs
51
52         /// <summary>
53         /// Provides the implementation of getting a member.  Derived classes can override
54         /// this method to customize behavior.  When not overridden the call site requesting the
55         /// binder determines the behavior.
56         /// </summary>
57         /// <param name="binder">The binder provided by the call site.</param>
58         /// <param name="result">The result of the get operation.</param>
59         /// <returns>true if the operation is complete, false if the call site should determine behavior.</returns>
60         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1007:UseGenericsWhereAppropriate")]
61         public virtual bool TryGetMember(GetMemberBinder binder, out object result) {
62             result = null;
63             return false;
64         }
65
66         /// <summary>
67         /// Provides the implementation of setting a member.  Derived classes can override
68         /// this method to customize behavior.  When not overridden the call site requesting the
69         /// binder determines the behavior.
70         /// </summary>
71         /// <param name="binder">The binder provided by the call site.</param>
72         /// <param name="value">The value to set.</param>
73         /// <returns>true if the operation is complete, false if the call site should determine behavior.</returns>
74         public virtual bool TrySetMember(SetMemberBinder binder, object value) {
75             return false;
76         }
77
78         /// <summary>
79         /// Provides the implementation of deleting a member.  Derived classes can override
80         /// this method to customize behavior.  When not overridden the call site requesting the
81         /// binder determines the behavior.
82         /// </summary>
83         /// <param name="binder">The binder provided by the call site.</param>
84         /// <returns>true if the operation is complete, false if the call site should determine behavior.</returns>
85         public virtual bool TryDeleteMember(DeleteMemberBinder binder) {
86             return false;
87         }
88
89         /// <summary>
90         /// Provides the implementation of calling a member.  Derived classes can override
91         /// this method to customize behavior.  When not overridden the call site requesting the
92         /// binder determines the behavior.
93         /// </summary>
94         /// <param name="binder">The binder provided by the call site.</param>
95         /// <param name="args">The arguments to be used for the invocation.</param>
96         /// <param name="result">The result of the invocation.</param>
97         /// <returns>true if the operation is complete, false if the call site should determine behavior.</returns>
98         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1007:UseGenericsWhereAppropriate")]
99         public virtual bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) {
100             result = null;
101             return false;
102         }
103
104         /// <summary>
105         /// Provides the implementation of converting the DynamicObject to another type.  Derived classes
106         /// can override this method to customize behavior.  When not overridden the call site
107         /// requesting the binder determines the behavior.
108         /// </summary>
109         /// <param name="binder">The binder provided by the call site.</param>
110         /// <param name="result">The result of the conversion.</param>
111         /// <returns>true if the operation is complete, false if the call site should determine behavior.</returns>
112         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1007:UseGenericsWhereAppropriate")]
113         public virtual bool TryConvert(ConvertBinder binder, out object result) {
114             result = null;
115             return false;
116         }
117
118         /// <summary>
119         /// Provides the implementation of creating an instance of the DynamicObject.  Derived classes
120         /// can override this method to customize behavior.  When not overridden the call site requesting
121         /// the binder determines the behavior.
122         /// </summary>
123         /// <param name="binder">The binder provided by the call site.</param>
124         /// <param name="args">The arguments used for creation.</param>
125         /// <param name="result">The created instance.</param>
126         /// <returns>true if the operation is complete, false if the call site should determine behavior.</returns>
127         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1007:UseGenericsWhereAppropriate")]
128         public virtual bool TryCreateInstance(CreateInstanceBinder binder, object[] args, out object result) {
129             result = null;
130             return false;
131         }
132
133         /// <summary>
134         /// Provides the implementation of invoking the DynamicObject.  Derived classes can
135         /// override this method to customize behavior.  When not overridden the call site requesting
136         /// the binder determines the behavior.
137         /// </summary>
138         /// <param name="binder">The binder provided by the call site.</param>
139         /// <param name="args">The arguments to be used for the invocation.</param>
140         /// <param name="result">The result of the invocation.</param>
141         /// <returns>true if the operation is complete, false if the call site should determine behavior.</returns>
142         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1007:UseGenericsWhereAppropriate")]
143         public virtual bool TryInvoke(InvokeBinder binder, object[] args, out object result) {
144             result = null;
145             return false;
146         }
147
148         /// <summary>
149         /// Provides the implementation of performing a binary operation.  Derived classes can
150         /// override this method to customize behavior.  When not overridden the call site requesting
151         /// the binder determines the behavior.
152         /// </summary>
153         /// <param name="binder">The binder provided by the call site.</param>
154         /// <param name="arg">The right operand for the operation.</param>
155         /// <param name="result">The result of the operation.</param>
156         /// <returns>true if the operation is complete, false if the call site should determine behavior.</returns>
157         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1007:UseGenericsWhereAppropriate")]
158         public virtual bool TryBinaryOperation(BinaryOperationBinder binder, object arg, out object result) {
159             result = null;
160             return false;
161         }
162
163         /// <summary>
164         /// Provides the implementation of performing a unary operation.  Derived classes can
165         /// override this method to customize behavior.  When not overridden the call site requesting
166         /// the binder determines the behavior.
167         /// </summary>
168         /// <param name="binder">The binder provided by the call site.</param>
169         /// <param name="result">The result of the operation.</param>
170         /// <returns>true if the operation is complete, false if the call site should determine behavior.</returns>
171         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1007:UseGenericsWhereAppropriate")]
172         public virtual bool TryUnaryOperation(UnaryOperationBinder binder, out object result) {
173             result = null;
174             return false;
175         }
176
177         /// <summary>
178         /// Provides the implementation of performing a get index operation.  Derived classes can
179         /// override this method to customize behavior.  When not overridden the call site requesting
180         /// the binder determines the behavior.
181         /// </summary>
182         /// <param name="binder">The binder provided by the call site.</param>
183         /// <param name="indexes">The indexes to be used.</param>
184         /// <param name="result">The result of the operation.</param>
185         /// <returns>true if the operation is complete, false if the call site should determine behavior.</returns>
186         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1007:UseGenericsWhereAppropriate")]
187         public virtual bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result) {
188             result = null;
189             return false;
190         }
191
192         /// <summary>
193         /// Provides the implementation of performing a set index operation.  Derived classes can
194         /// override this method to custmize behavior.  When not overridden the call site requesting
195         /// the binder determines the behavior.
196         /// </summary>
197         /// <param name="binder">The binder provided by the call site.</param>
198         /// <param name="indexes">The indexes to be used.</param>
199         /// <param name="value">The value to set.</param>
200         /// <returns>true if the operation is complete, false if the call site should determine behavior.</returns>
201         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1007:UseGenericsWhereAppropriate")]
202         public virtual bool TrySetIndex(SetIndexBinder binder, object[] indexes, object value) {
203             return false;
204         }
205
206         /// <summary>
207         /// Provides the implementation of performing a delete index operation.  Derived classes
208         /// can override this method to custmize behavior.  When not overridden the call site
209         /// requesting the binder determines the behavior.
210         /// </summary>
211         /// <param name="binder">The binder provided by the call site.</param>
212         /// <param name="indexes">The indexes to be deleted.</param>
213         /// <returns>true if the operation is complete, false if the call site should determine behavior.</returns>
214         public virtual bool TryDeleteIndex(DeleteIndexBinder binder, object[] indexes) {
215             return false;
216         }
217
218         /// <summary>
219         /// Returns the enumeration of all dynamic member names.
220         /// </summary>
221         /// <returns>The list of dynamic member names.</returns>
222         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")]
223         public virtual System.Collections.Generic.IEnumerable<string> GetDynamicMemberNames() {
224             return new string[0];
225         }
226         #endregion
227
228         #region MetaDynamic
229
230         private sealed class MetaDynamic : DynamicMetaObject {
231
232             internal MetaDynamic(Expression expression, DynamicObject value)
233                 : base(expression, BindingRestrictions.Empty, value) {
234             }
235
236             public override System.Collections.Generic.IEnumerable<string> GetDynamicMemberNames()
237             {
238                 return Value.GetDynamicMemberNames();
239             }
240
241             public override DynamicMetaObject BindGetMember(GetMemberBinder binder) {
242                 if (IsOverridden("TryGetMember")) {
243                     return CallMethodWithResult("TryGetMember", binder, NoArgs, (e) => binder.FallbackGetMember(this, e));
244                 }
245
246                 return base.BindGetMember(binder);
247             }
248
249             public override DynamicMetaObject BindSetMember(SetMemberBinder binder, DynamicMetaObject value) {
250                 if (IsOverridden("TrySetMember")) {
251                     return CallMethodReturnLast("TrySetMember", binder, GetArgs(value), (e) => binder.FallbackSetMember(this, value, e));
252                 }
253
254                 return base.BindSetMember(binder, value);
255             }
256
257             public override DynamicMetaObject BindDeleteMember(DeleteMemberBinder binder) {
258                 if (IsOverridden("TryDeleteMember")) {
259                     return CallMethodNoResult("TryDeleteMember", binder, NoArgs, (e) => binder.FallbackDeleteMember(this, e));
260                 }
261
262                 return base.BindDeleteMember(binder);
263             }
264
265             public override DynamicMetaObject BindConvert(ConvertBinder binder) {
266                 if (IsOverridden("TryConvert")) {
267                     return CallMethodWithResult("TryConvert", binder, NoArgs, (e) => binder.FallbackConvert(this, e));
268                 }
269
270                 return base.BindConvert(binder);
271             }
272
273             public override DynamicMetaObject BindInvokeMember(InvokeMemberBinder binder, DynamicMetaObject[] args) {
274                 if (IsOverridden("TryInvokeMember")) {
275                     return CallMethodWithResult("TryInvokeMember", binder, GetArgArray(args), (e) => binder.FallbackInvokeMember(this, args, e));
276                 } else if (IsOverridden("TryGetMember")) {
277                     // Generate a tree like:
278                     //
279                     // {
280                     //   object result;
281                     //   TryGetMember(payload, out result) ? FallbackInvoke(result) : fallbackResult
282                     // }
283                     //
284                     // Then it calls FallbackInvokeMember with this tree as the
285                     // "error", giving the language the option of using this
286                     // tree or doing .NET binding.
287                     //
288                     return CallMethodWithResult(
289                         "TryGetMember", new GetBinderAdapter(binder), NoArgs,
290                         (e) => binder.FallbackInvokeMember(this, args, e),
291                         (e) => binder.FallbackInvoke(e, args, null)
292                     );
293                 }
294
295                 return base.BindInvokeMember(binder, args);
296             }
297
298
299             public override DynamicMetaObject BindCreateInstance(CreateInstanceBinder binder, DynamicMetaObject[] args) {
300                 if (IsOverridden("TryCreateInstance")) {
301                     return CallMethodWithResult("TryCreateInstance", binder, GetArgArray(args), (e) => binder.FallbackCreateInstance(this, args, e));
302                 }
303
304                 return base.BindCreateInstance(binder, args);
305             }
306
307             public override DynamicMetaObject BindInvoke(InvokeBinder binder, DynamicMetaObject[] args) {
308                 if (IsOverridden("TryInvoke")) {
309                     return CallMethodWithResult("TryInvoke", binder, GetArgArray(args), (e) => binder.FallbackInvoke(this, args, e));
310                 }
311
312                 return base.BindInvoke(binder, args);
313             }
314
315             public override DynamicMetaObject BindBinaryOperation(BinaryOperationBinder binder, DynamicMetaObject arg) {
316                 if (IsOverridden("TryBinaryOperation")) {
317                     return CallMethodWithResult("TryBinaryOperation", binder, GetArgs(arg), (e) => binder.FallbackBinaryOperation(this, arg, e));
318                 }
319
320                 return base.BindBinaryOperation(binder, arg);
321             }
322
323             public override DynamicMetaObject BindUnaryOperation(UnaryOperationBinder binder) {
324                 if (IsOverridden("TryUnaryOperation")) {
325                     return CallMethodWithResult("TryUnaryOperation", binder, NoArgs, (e) => binder.FallbackUnaryOperation(this, e));
326                 }
327
328                 return base.BindUnaryOperation(binder);
329             }
330
331             public override DynamicMetaObject BindGetIndex(GetIndexBinder binder, DynamicMetaObject[] indexes) {
332                 if (IsOverridden("TryGetIndex")) {
333                     return CallMethodWithResult("TryGetIndex", binder, GetArgArray(indexes), (e) => binder.FallbackGetIndex(this, indexes, e));
334                 }
335
336                 return base.BindGetIndex(binder, indexes);
337             }
338
339             public override DynamicMetaObject BindSetIndex(SetIndexBinder binder, DynamicMetaObject[] indexes, DynamicMetaObject value) {
340                 if (IsOverridden("TrySetIndex")) {
341                     return CallMethodReturnLast("TrySetIndex", binder, GetArgArray(indexes, value), (e) => binder.FallbackSetIndex(this, indexes, value, e));
342                 }
343
344                 return base.BindSetIndex(binder, indexes, value);
345             }
346
347             public override DynamicMetaObject BindDeleteIndex(DeleteIndexBinder binder, DynamicMetaObject[] indexes) {
348                 if (IsOverridden("TryDeleteIndex")) {
349                     return CallMethodNoResult("TryDeleteIndex", binder, GetArgArray(indexes), (e) => binder.FallbackDeleteIndex(this, indexes, e));
350                 }
351
352                 return base.BindDeleteIndex(binder, indexes);
353             }
354
355             private delegate DynamicMetaObject Fallback(DynamicMetaObject errorSuggestion);
356
357             private readonly static Expression[] NoArgs = new Expression[0];
358
359             private static Expression[] GetArgs(params DynamicMetaObject[] args) {
360                 Expression[] paramArgs = DynamicMetaObject.GetExpressions(args);
361
362                 for (int i = 0; i < paramArgs.Length; i++) {
363                     paramArgs[i] = Expression.Convert(args[i].Expression, typeof(object));
364                 }
365
366                 return paramArgs;
367             }
368
369             private static Expression[] GetArgArray(DynamicMetaObject[] args) {
370                 return new[] { Expression.NewArrayInit(typeof(object), GetArgs(args)) };
371             }
372
373             private static Expression[] GetArgArray(DynamicMetaObject[] args, DynamicMetaObject value) {
374                 return new Expression[] {
375                     Expression.NewArrayInit(typeof(object), GetArgs(args)),
376                     Expression.Convert(value.Expression, typeof(object))
377                 };
378             }
379
380             private static ConstantExpression Constant(DynamicMetaObjectBinder binder) {
381                 Type t = binder.GetType();
382                 while (!t.IsVisible) {
383                     t = t.BaseType;
384                 }
385                 return Expression.Constant(binder, t);
386             }
387
388             /// <summary>
389             /// Helper method for generating a MetaObject which calls a
390             /// specific method on Dynamic that returns a result
391             /// </summary>
392             private DynamicMetaObject CallMethodWithResult(string methodName, DynamicMetaObjectBinder binder, Expression[] args, Fallback fallback) {
393                 return CallMethodWithResult(methodName, binder, args, fallback, null);
394             }
395
396             /// <summary>
397             /// Helper method for generating a MetaObject which calls a
398             /// specific method on Dynamic that returns a result
399             /// </summary>
400             private DynamicMetaObject CallMethodWithResult(string methodName, DynamicMetaObjectBinder binder, Expression[] args, Fallback fallback, Fallback fallbackInvoke) {
401                 //
402                 // First, call fallback to do default binding
403                 // This produces either an error or a call to a .NET member
404                 //
405                 DynamicMetaObject fallbackResult = fallback(null);
406
407                 //
408                 // Build a new expression like:
409                 // {
410                 //   object result;
411                 //   TryGetMember(payload, out result) ? fallbackInvoke(result) : fallbackResult
412                 // }
413                 //
414                 var result = Expression.Parameter(typeof(object), null);
415
416                 var callArgs = new Expression[args.Length + 2];
417                 Array.Copy(args, 0, callArgs, 1, args.Length);
418                 callArgs[0] = Constant(binder);
419                 callArgs[callArgs.Length - 1] = result;
420
421                 var resultMO = new DynamicMetaObject(result, BindingRestrictions.Empty);
422
423                 // Need to add a conversion if calling TryConvert
424                 if (binder.ReturnType != typeof(object)) {
425                     Debug.Assert(binder is ConvertBinder && fallbackInvoke == null);
426
427                     var convert = Expression.Convert(resultMO.Expression, binder.ReturnType);
428                     // will always be a cast or unbox
429                     Debug.Assert(convert.Method == null);
430
431                     resultMO = new DynamicMetaObject(convert, resultMO.Restrictions);
432                 }
433
434                 if (fallbackInvoke != null) {
435                     resultMO = fallbackInvoke(resultMO);
436                 }
437
438                 var callDynamic = new DynamicMetaObject(
439                     Expression.Block(
440                         new[] { result },
441                         Expression.Condition(
442                             Expression.Call(
443                                 GetLimitedSelf(),
444                                 typeof(DynamicObject).GetMethod(methodName),
445                                 callArgs
446                             ),
447                             resultMO.Expression,
448                             fallbackResult.Expression,
449                             binder.ReturnType
450                         )
451                     ),
452                     GetRestrictions().Merge(resultMO.Restrictions).Merge(fallbackResult.Restrictions)
453                 );
454                 
455                 //
456                 // Now, call fallback again using our new MO as the error
457                 // When we do this, one of two things can happen:
458                 //   1. Binding will succeed, and it will ignore our call to
459                 //      the dynamic method, OR
460                 //   2. Binding will fail, and it will use the MO we created
461                 //      above.
462                 //
463                 return fallback(callDynamic);
464             }
465
466
467             /// <summary>
468             /// Helper method for generating a MetaObject which calls a
469             /// specific method on Dynamic, but uses one of the arguments for
470             /// the result.
471             /// </summary>
472             private DynamicMetaObject CallMethodReturnLast(string methodName, DynamicMetaObjectBinder binder, Expression[] args, Fallback fallback) {
473                 //
474                 // First, call fallback to do default binding
475                 // This produces either an error or a call to a .NET member
476                 //
477                 DynamicMetaObject fallbackResult = fallback(null);
478
479                 //
480                 // Build a new expression like:
481                 // {
482                 //   object result;
483                 //   TrySetMember(payload, result = value) ? result : fallbackResult
484                 // }
485                 //
486
487                 var result = Expression.Parameter(typeof(object), null);
488                 var callArgs = args.AddFirst(Constant(binder));
489                 callArgs[args.Length] = Expression.Assign(result, callArgs[args.Length]);
490
491                 var callDynamic = new DynamicMetaObject(
492                     Expression.Block(
493                         new[] { result },
494                         Expression.Condition(
495                             Expression.Call(
496                                 GetLimitedSelf(),
497                                 typeof(DynamicObject).GetMethod(methodName),
498                                 callArgs
499                             ),
500                             result,
501                             fallbackResult.Expression,
502                             typeof(object)
503                         )
504                     ),
505                     GetRestrictions().Merge(fallbackResult.Restrictions)
506                 );
507
508                 //
509                 // Now, call fallback again using our new MO as the error
510                 // When we do this, one of two things can happen:
511                 //   1. Binding will succeed, and it will ignore our call to
512                 //      the dynamic method, OR
513                 //   2. Binding will fail, and it will use the MO we created
514                 //      above.
515                 //
516                 return fallback(callDynamic);
517             }
518
519
520             /// <summary>
521             /// Helper method for generating a MetaObject which calls a
522             /// specific method on Dynamic, but uses one of the arguments for
523             /// the result.
524             /// </summary>
525             private DynamicMetaObject CallMethodNoResult(string methodName, DynamicMetaObjectBinder binder, Expression[] args, Fallback fallback) {
526                 //
527                 // First, call fallback to do default binding
528                 // This produces either an error or a call to a .NET member
529                 //
530                 DynamicMetaObject fallbackResult = fallback(null);
531
532                 //
533                 // Build a new expression like:
534                 //   if (TryDeleteMember(payload)) { } else { fallbackResult }
535                 //
536                 var callDynamic = new DynamicMetaObject(
537                     Expression.Condition(
538                         Expression.Call(
539                             GetLimitedSelf(),
540                             typeof(DynamicObject).GetMethod(methodName),
541                             args.AddFirst(Constant(binder))
542                         ),
543                         Expression.Empty(),
544                         fallbackResult.Expression,
545                         typeof(void)
546                     ),
547                     GetRestrictions().Merge(fallbackResult.Restrictions)
548                 );
549
550                 //
551                 // Now, call fallback again using our new MO as the error
552                 // When we do this, one of two things can happen:
553                 //   1. Binding will succeed, and it will ignore our call to
554                 //      the dynamic method, OR
555                 //   2. Binding will fail, and it will use the MO we created
556                 //      above.
557                 //
558                 return fallback(callDynamic);
559             }
560
561             /// <summary>
562             /// Checks if the derived type has overridden the specified method.  If there is no
563             /// implementation for the method provided then Dynamic falls back to the base class
564             /// behavior which lets the call site determine how the binder is performed.
565             /// </summary>
566             private bool IsOverridden(string method) {
567                 var methods = Value.GetType().GetMember(method, MemberTypes.Method, BindingFlags.Public | BindingFlags.Instance);
568
569                 foreach (MethodInfo mi in methods) {
570                     if (mi.DeclaringType != typeof(DynamicObject) && mi.GetBaseDefinition().DeclaringType == typeof(DynamicObject)) {
571                         return true;
572                     }
573                 }
574
575                 return false;
576             }
577
578             /// <summary>
579             /// Returns a Restrictions object which includes our current restrictions merged
580             /// with a restriction limiting our type
581             /// </summary>
582             private BindingRestrictions GetRestrictions() {
583                 Debug.Assert(Restrictions == BindingRestrictions.Empty, "We don't merge, restrictions are always empty");
584
585                 return BindingRestrictions.GetTypeRestriction(this);
586             }
587
588             /// <summary>
589             /// Returns our Expression converted to our known LimitType
590             /// </summary>
591             private Expression GetLimitedSelf() {
592                 if (TypeUtils.AreEquivalent(Expression.Type, LimitType)) {
593                     return Expression;
594                 }
595                 return Expression.Convert(Expression, LimitType);
596             }
597
598             private new DynamicObject Value {
599                 get {
600                     return (DynamicObject)base.Value;
601                 }
602             }
603
604             // It is okay to throw NotSupported from this binder. This object
605             // is only used by DynamicObject.GetMember--it is not expected to
606             // (and cannot) implement binding semantics. It is just so the DO
607             // can use the Name and IgnoreCase properties.
608             private sealed class GetBinderAdapter : GetMemberBinder {
609                 internal GetBinderAdapter(InvokeMemberBinder binder)
610                     : base(binder.Name, binder.IgnoreCase) {
611                 }
612
613                 public override DynamicMetaObject FallbackGetMember(DynamicMetaObject target, DynamicMetaObject errorSuggestion) {
614                     throw new NotSupportedException();
615                 }
616             }
617         }
618
619         #endregion
620
621         #region IDynamicMetaObjectProvider Members
622
623         /// <summary>
624         /// The provided MetaObject will dispatch to the Dynamic virtual methods.
625         /// The object can be encapsulated inside of another MetaObject to
626         /// provide custom behavior for individual actions.
627         /// </summary>
628         public virtual DynamicMetaObject GetMetaObject(Expression parameter) {
629             return new MetaDynamic(parameter, this);
630         }
631
632         #endregion
633     }
634 }