PageRenderTime 216ms CodeModel.GetById 179ms app.highlight 29ms RepoModel.GetById 1ms app.codeStats 1ms

/Microsoft.Scripting.Core/Actions/DynamicObject.cs

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