PageRenderTime 49ms CodeModel.GetById 2ms app.highlight 41ms RepoModel.GetById 1ms app.codeStats 0ms

/IronPython_Main/Runtime/Microsoft.Scripting/Runtime/DynamicOperations.cs

#
C# | 683 lines | 383 code | 87 blank | 213 comment | 24 complexity | 9fa116f30b8d37ebeeff012b15c34d1a MD5 | raw file
  1/* ****************************************************************************
  2 *
  3 * Copyright (c) Microsoft Corporation. 
  4 *
  5 * This source code is subject to terms and conditions of the Apache License, Version 2.0. 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  Apache License, Version 2.0, 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 Apache License, Version 2.0.
 10 *
 11 * You must not remove this notice, or any other, from this software.
 12 *
 13 *
 14 * ***************************************************************************/
 15
 16#if !CLR2
 17using System.Linq.Expressions;
 18#else
 19using Microsoft.Scripting.Ast;
 20#endif
 21
 22using System;
 23using System.Collections.Generic;
 24using System.Diagnostics;
 25using System.Dynamic;
 26using System.Reflection;
 27using System.Runtime.CompilerServices;
 28
 29using Microsoft.Contracts;
 30using Microsoft.Scripting.Utils;
 31
 32namespace Microsoft.Scripting.Runtime {
 33
 34    /// <summary>
 35    /// ObjectOperations provide a large catalogue of object operations such as member access, conversions, 
 36    /// indexing, and things like addition.  There are several introspection and tool support services available
 37    /// for more advanced hosts.  
 38    /// 
 39    /// You get ObjectOperation instances from ScriptEngine, and they are bound to their engines for the semantics 
 40    /// of the operations.  There is a default instance of ObjectOperations you can share across all uses of the 
 41    /// engine.  However, very advanced hosts can create new instances.
 42    /// </summary>
 43    public sealed class DynamicOperations {
 44        private readonly LanguageContext _lc;
 45
 46        /// <summary> a dictionary of SiteKey's which are used to cache frequently used operations, logically a set </summary>
 47        private Dictionary<SiteKey, SiteKey> _sites = new Dictionary<SiteKey, SiteKey>();
 48
 49        /// <summary> the # of sites we had created at the last cleanup </summary>
 50        private int LastCleanup;
 51
 52        /// <summary> the total number of sites we've ever created </summary>
 53        private int SitesCreated;
 54
 55        /// <summary> the number of sites required before we'll try cleaning up the cache... </summary>
 56        private const int CleanupThreshold = 20;
 57
 58        /// <summary> the minimum difference between the average that is required to remove </summary>
 59        private const int RemoveThreshold = 2;
 60
 61        /// <summary> the maximum number we'll remove on a single cache cleanup </summary>
 62        private const int StopCleanupThreshold = CleanupThreshold / 2;
 63
 64        /// <summary> the number of sites we should clear after if we can't make progress cleaning up otherwise </summary>
 65        private const int ClearThreshold = 50;
 66
 67        public DynamicOperations(LanguageContext lc) {
 68            ContractUtils.RequiresNotNull(lc, "lc");
 69            _lc = lc;
 70        }
 71
 72        #region Basic Operations
 73
 74        private Dictionary<int, Func<DynamicOperations, CallSiteBinder, object, object[], object>> _invokers = new Dictionary<int, Func<DynamicOperations, CallSiteBinder, object, object[], object>>();
 75
 76        /// <summary>
 77        /// Calls the provided object with the given parameters and returns the result.
 78        /// 
 79        /// The prefered way of calling objects is to convert the object to a strongly typed delegate 
 80        /// using the ConvertTo methods and then invoking that delegate.
 81        /// </summary>
 82        public object Invoke(object obj, params object[] parameters) {
 83            return GetInvoker(parameters.Length)(this, _lc.CreateInvokeBinder(new CallInfo(parameters.Length)), obj, parameters);
 84        }
 85        
 86        /// <summary>
 87        /// Invokes a member on the provided object with the given parameters and returns the result.
 88        /// </summary>
 89        public object InvokeMember(object obj, string memberName, params object[] parameters) {
 90            return InvokeMember(obj, memberName, false, parameters);
 91        }
 92
 93        /// <summary>
 94        /// Invokes a member on the provided object with the given parameters and returns the result.
 95        /// </summary>
 96        public object InvokeMember(object obj, string memberName, bool ignoreCase, params object[] parameters) {
 97            return GetInvoker(parameters.Length)(this, _lc.CreateCallBinder(memberName, ignoreCase, new CallInfo(parameters.Length)), obj, parameters);
 98        }
 99
100        /// <summary>
101        /// Creates a new instance from the provided object using the given parameters, and returns the result.
102        /// </summary>
103        public object CreateInstance(object obj, params object[] parameters) {
104            return GetInvoker(parameters.Length)(this, _lc.CreateCreateBinder(new CallInfo(parameters.Length)), obj, parameters);
105        }
106
107        /// <summary>
108        /// Gets the member name from the object obj.  Throws an exception if the member does not exist or is write-only.
109        /// </summary>
110        public object GetMember(object obj, string name) {
111            return GetMember(obj, name, false);
112        }
113
114        /// <summary>
115        /// Gets the member name from the object obj and converts it to the type T.  Throws an exception if the
116        /// member does not exist, is write-only, or cannot be converted.
117        /// </summary>
118        public T GetMember<T>(object obj, string name) {
119            return GetMember<T>(obj, name, false);
120        }
121
122        /// <summary>
123        /// Gets the member name from the object obj.  Returns true if the member is successfully retrieved and 
124        /// stores the value in the value out param.
125        /// </summary>
126        public bool TryGetMember(object obj, string name, out object value) {
127            return TryGetMember(obj, name, false, out value);
128        }
129
130        /// <summary>
131        /// Returns true if the object has a member named name, false if the member does not exist.
132        /// </summary>
133        public bool ContainsMember(object obj, string name) {
134            return ContainsMember(obj, name, false);
135        }
136
137        /// <summary>
138        /// Removes the member name from the object obj.
139        /// </summary>
140        public void RemoveMember(object obj, string name) {
141            RemoveMember(obj, name, false);
142        }
143
144        /// <summary>
145        /// Sets the member name on object obj to value.
146        /// </summary>
147        public void SetMember(object obj, string name, object value) {
148            SetMember(obj, name, value, false);
149        }
150
151        /// <summary>
152        /// Sets the member name on object obj to value.  This overload can be used to avoid
153        /// boxing and casting of strongly typed members.
154        /// </summary>
155        public void SetMember<T>(object obj, string name, T value) {
156            SetMember<T>(obj, name, value, false);
157        }
158
159        /// <summary>
160        /// Gets the member name from the object obj.  Throws an exception if the member does not exist or is write-only.
161        /// </summary>
162        public object GetMember(object obj, string name, bool ignoreCase) {
163            CallSite<Func<CallSite, object, object>> site;
164            site = GetOrCreateSite<object, object>(_lc.CreateGetMemberBinder(name, ignoreCase));
165            return site.Target(site, obj);
166        }
167
168        /// <summary>
169        /// Gets the member name from the object obj and converts it to the type T. The conversion will be explicit or implicit
170        /// depending on what the langauge prefers. Throws an exception if the member does not exist, is write-only, or cannot be converted.
171        /// </summary>
172        public T GetMember<T>(object obj, string name, bool ignoreCase) {
173            CallSite<Func<CallSite, object, T>> convertSite = GetOrCreateSite<object, T>(_lc.CreateConvertBinder(typeof(T), null));
174            CallSite<Func<CallSite, object, object>> site = GetOrCreateSite<object, object>(_lc.CreateGetMemberBinder(name, ignoreCase));
175            return convertSite.Target(convertSite, site.Target(site, obj));
176        }
177
178        /// <summary>
179        /// Gets the member name from the object obj.  Returns true if the member is successfully retrieved and 
180        /// stores the value in the value out param.
181        /// </summary>
182        public bool TryGetMember(object obj, string name, bool ignoreCase, out object value) {
183            try {
184                value = GetMember(obj, name, ignoreCase);
185                return true;
186            } catch (MissingMemberException) {
187                value = null;
188                return false;
189            }
190        }
191
192        /// <summary>
193        /// Returns true if the object has a member named name, false if the member does not exist.
194        /// </summary>
195        public bool ContainsMember(object obj, string name, bool ignoreCase) {
196            object dummy;
197            return TryGetMember(obj, name, ignoreCase, out dummy);
198        }
199
200        /// <summary>
201        /// Removes the member name from the object obj.  Returns true if the member was successfully removed
202        /// or false if the member does not exist.
203        /// </summary>
204        public void RemoveMember(object obj, string name, bool ignoreCase) {
205            CallSite<Action<CallSite, object>> site;
206            site = GetOrCreateActionSite<object>(_lc.CreateDeleteMemberBinder(name, ignoreCase));
207            site.Target(site, obj);
208        }
209
210        /// <summary>
211        /// Sets the member name on object obj to value.
212        /// </summary>
213        public void SetMember(object obj, string name, object value, bool ignoreCase) {
214            CallSite<Func<CallSite, object, object, object>> site;
215            site = GetOrCreateSite<object, object, object>(_lc.CreateSetMemberBinder(name, ignoreCase));
216            site.Target(site, obj, value);
217        }
218
219        /// <summary>
220        /// Sets the member name on object obj to value.  This overload can be used to avoid
221        /// boxing and casting of strongly typed members.
222        /// </summary>
223        public void SetMember<T>(object obj, string name, T value, bool ignoreCase) {
224            CallSite<Func<CallSite, object, T, object>> site;
225            site = GetOrCreateSite<object, T, object>(_lc.CreateSetMemberBinder(name, ignoreCase));
226            site.Target(site, obj, value);
227        }
228
229        /// <summary>
230        /// Converts the object obj to the type T.  The conversion will be explicit or implicit
231        /// depending on what the langauge prefers.
232        /// </summary>
233        public T ConvertTo<T>(object obj) {
234            CallSite<Func<CallSite, object, T>> site;
235            site = GetOrCreateSite<object, T>(_lc.CreateConvertBinder(typeof(T), null));
236            return site.Target(site, obj);
237        }
238
239        /// <summary> 
240        /// Converts the object obj to the type type.  The conversion will be explicit or implicit
241        /// depending on what the langauge prefers.
242        /// </summary>
243        public object ConvertTo(object obj, Type type) {
244            if (type.IsInterface || type.IsClass) {
245                CallSite<Func<CallSite, object, object>> site;
246                site = GetOrCreateSite<object, object>(_lc.CreateConvertBinder(type, null));
247                return site.Target(site, obj);
248            }
249
250            // TODO: We should probably cache these instead of using reflection all the time.
251            foreach (MethodInfo mi in typeof(DynamicOperations).GetMember("ConvertTo")) {
252                if (mi.IsGenericMethod) {
253                    try {
254                        return mi.MakeGenericMethod(type).Invoke(this, new object[] { obj });
255                    } catch(TargetInvocationException tie) {
256                        throw tie.InnerException;
257                    }
258                }
259            }
260
261            throw new InvalidOperationException();
262        }
263
264        /// <summary>
265        /// Converts the object obj to the type T.  Returns true if the value can be converted, false if it cannot.
266        /// 
267        /// The conversion will be explicit or implicit depending on what the langauge prefers.
268        /// </summary>
269        public bool TryConvertTo<T>(object obj, out T result) {
270            try {
271                result = ConvertTo<T>(obj);
272                return true;
273            } catch (ArgumentTypeException) {
274                result = default(T);
275                return false;
276            } catch (InvalidCastException) {
277                result = default(T);
278                return false;
279            }
280        }
281
282        /// <summary>
283        /// Converts the object obj to the type type.  Returns true if the value can be converted, false if it cannot.
284        /// 
285        /// The conversion will be explicit or implicit depending on what the langauge prefers.
286        /// </summary>
287        public bool TryConvertTo(object obj, Type type, out object result) {
288            try {
289                result = ConvertTo(obj, type);
290                return true;
291            } catch (ArgumentTypeException) {
292                result = null;
293                return false;
294            } catch (InvalidCastException) {
295                result = null;
296                return false;
297            }
298        }
299
300        /// <summary>
301        /// Convers the object obj to the type T including explicit conversions which may lose information.
302        /// </summary>
303        public T ExplicitConvertTo<T>(object obj) {
304            CallSite<Func<CallSite, object, T>> site;
305            site = GetOrCreateSite<object, T>(_lc.CreateConvertBinder(typeof(T), true));
306            return site.Target(site, obj);
307        }
308
309        /// <summary>
310        /// Converts the object obj to the type type including explicit conversions which may lose information.
311        /// </summary>
312        public object ExplicitConvertTo(object obj, Type type) {
313            CallSite<Func<CallSite, object, object>> site;
314            site = GetOrCreateSite<object, object>(_lc.CreateConvertBinder(type, true));
315            return site.Target(site, obj);
316        }
317
318        /// <summary>
319        /// Converts the object obj to the type type including explicit conversions which may lose information.  
320        /// 
321        /// Returns true if the value can be converted, false if it cannot.
322        /// </summary>
323        public bool TryExplicitConvertTo(object obj, Type type, out object result) {
324            try {
325                result = ExplicitConvertTo(obj, type);
326                return true;
327            } catch (ArgumentTypeException) {
328                result = null;
329                return false;
330            } catch (InvalidCastException) {
331                result = null;
332                return false;
333            }
334        }
335
336        /// <summary>
337        /// Converts the object obj to the type T.  Returns true if the value can be converted, false if it cannot.
338        /// </summary>
339        public bool TryExplicitConvertTo<T>(object obj, out T result) {
340            try {
341                result = ExplicitConvertTo<T>(obj);
342                return true;
343            } catch (ArgumentTypeException) {
344                result = default(T);
345                return false;
346            } catch (InvalidCastException) {
347                result = default(T);
348                return false;
349            }
350        }
351
352        /// <summary>
353        /// Convers the object obj to the type T including implicit conversions.
354        /// </summary>
355        public T ImplicitConvertTo<T>(object obj) {
356            CallSite<Func<CallSite, object, T>> site;
357            site = GetOrCreateSite<object, T>(_lc.CreateConvertBinder(typeof(T), false));
358            return site.Target(site, obj);
359        }
360
361        /// <summary>
362        /// Converts the object obj to the type type including implicit conversions.
363        /// </summary>
364        public object ImplicitConvertTo(object obj, Type type) {
365            CallSite<Func<CallSite, object, object>> site;
366            site = GetOrCreateSite<object, object>(_lc.CreateConvertBinder(type, false));
367            return site.Target(site, obj);
368        }
369
370        /// <summary>
371        /// Converts the object obj to the type type including implicit conversions. 
372        /// 
373        /// Returns true if the value can be converted, false if it cannot.
374        /// </summary>
375        public bool TryImplicitConvertTo(object obj, Type type, out object result) {
376            try {
377                result = ImplicitConvertTo(obj, type);
378                return true;
379            } catch (ArgumentTypeException) {
380                result = null;
381                return false;
382            } catch (InvalidCastException) {
383                result = null;
384                return false;
385            }
386        }
387
388        /// <summary>
389        /// Converts the object obj to the type T.  Returns true if the value can be converted, false if it cannot.
390        /// </summary>
391        public bool TryImplicitConvertTo<T>(object obj, out T result) {
392            try {
393                result = ImplicitConvertTo<T>(obj);
394                return true;
395            } catch (ArgumentTypeException) {
396                result = default(T);
397                return false;
398            } catch (InvalidCastException) {
399                result = default(T);
400                return false;
401            }
402        }
403
404        /// <summary>
405        /// Performs a generic unary operation on the strongly typed target and returns the value as the specified type
406        /// </summary>
407        public TResult DoOperation<TTarget, TResult>(ExpressionType operation, TTarget target) {
408            var site = GetOrCreateSite<TTarget, TResult>(_lc.CreateUnaryOperationBinder(operation));
409            return site.Target(site, target);
410        }
411
412        /// <summary>
413        /// Peforms the generic binary operation on the specified strongly typed targets and returns
414        /// the strongly typed result.
415        /// </summary>
416        public TResult DoOperation<TTarget, TOther, TResult>(ExpressionType operation, TTarget target, TOther other) {
417            var site = GetOrCreateSite<TTarget, TOther, TResult>(_lc.CreateBinaryOperationBinder(operation));
418            return site.Target(site, target, other);
419        }
420        
421        public string GetDocumentation(object o) {
422            return _lc.GetDocumentation(o);
423        }
424
425        public IList<string> GetCallSignatures(object o) {
426            return _lc.GetCallSignatures(o);
427        }
428
429        public bool IsCallable(object o) {
430            return _lc.IsCallable(o);
431        }
432        
433        /// <summary>
434        /// Returns a list of strings which contain the known members of the object.
435        /// </summary>
436        public IList<string> GetMemberNames(object obj) {
437            return _lc.GetMemberNames(obj);
438        }
439
440        /// <summary>
441        /// Returns a string representation of the object in a language specific object display format.
442        /// </summary>
443        public string Format(object obj) {
444            return _lc.FormatObject(this, obj);
445        }
446
447        #endregion
448
449        #region Private implementation details
450
451        /// <summary>
452        /// Gets or creates a dynamic site w/ the specified type parameters for the provided binder.
453        /// </summary>
454        /// <remarks>
455        /// This will either get the site from the cache or create a new site and return it. The cache
456        /// may be cleaned if it's gotten too big since the last usage.
457        /// </remarks>
458        public CallSite<Func<CallSite, T1, TResult>> GetOrCreateSite<T1, TResult>(CallSiteBinder siteBinder) {
459            return GetOrCreateSite<CallSite<Func<CallSite, T1, TResult>>>(siteBinder, CallSite<Func<CallSite, T1, TResult>>.Create);
460        }
461
462        /// <summary>
463        /// Gets or creates a dynamic site w/ the specified type parameters for the provided binder.
464        /// </summary>
465        /// <remarks>
466        /// This will either get the site from the cache or create a new site and return it. The cache
467        /// may be cleaned if it's gotten too big since the last usage.
468        /// </remarks>
469        public CallSite<Action<CallSite, T1>> GetOrCreateActionSite<T1>(CallSiteBinder siteBinder) {
470            return GetOrCreateSite<CallSite<Action<CallSite, T1>>>(siteBinder, CallSite<Action<CallSite, T1>>.Create);
471        }
472
473        /// <summary>
474        /// Gets or creates a dynamic site w/ the specified type parameters for the provided binder.
475        /// </summary>
476        /// <remarks>
477        /// This will either get the site from the cache or create a new site and return it. The cache
478        /// may be cleaned if it's gotten too big since the last usage.
479        /// </remarks>
480        public CallSite<Func<CallSite, T1, T2, TResult>> GetOrCreateSite<T1, T2, TResult>(CallSiteBinder siteBinder) {
481            return GetOrCreateSite<CallSite<Func<CallSite, T1, T2, TResult>>>(siteBinder, CallSite<Func<CallSite, T1, T2, TResult>>.Create);
482        }
483
484        /// <summary>
485        /// Gets or creates a dynamic site w/ the specified type parameters for the provided binder.
486        /// </summary>
487        /// <remarks>
488        /// This will either get the site from the cache or create a new site and return it. The cache
489        /// may be cleaned if it's gotten too big since the last usage.
490        /// </remarks>
491        public CallSite<Func<CallSite, T1, T2, T3, TResult>> GetOrCreateSite<T1, T2, T3, TResult>(CallSiteBinder siteBinder) {
492            return GetOrCreateSite<CallSite<Func<CallSite, T1, T2, T3, TResult>>>(siteBinder, CallSite<Func<CallSite, T1, T2, T3, TResult>>.Create);
493        }
494
495        /// <summary>
496        /// Gets or creates a dynamic site w/ the specified type parameters for the provided binder.
497        /// </summary>
498        /// <remarks>
499        /// This will either get the site from the cache or create a new site and return it. The cache
500        /// may be cleaned if it's gotten too big since the last usage.
501        /// </remarks>
502        public CallSite<TSiteFunc> GetOrCreateSite<TSiteFunc>(CallSiteBinder siteBinder) where TSiteFunc : class {
503            return GetOrCreateSite<CallSite<TSiteFunc>>(siteBinder, CallSite<TSiteFunc>.Create);
504        }
505
506        /// <summary>
507        /// Helper to create to get or create the dynamic site - called by the GetSite methods.
508        /// </summary>
509        private T GetOrCreateSite<T>(CallSiteBinder siteBinder, Func<CallSiteBinder, T> factory) where T : CallSite {
510            SiteKey sk = new SiteKey(typeof(T), siteBinder);
511
512            lock (_sites) {
513                SiteKey old;
514                if (!_sites.TryGetValue(sk, out old)) {
515                    SitesCreated++;
516                    if (SitesCreated < 0) {
517                        // overflow, just reset back to zero...
518                        SitesCreated = 0;
519                        LastCleanup = 0;
520                    }
521                    sk.Site = factory(sk.SiteBinder);
522                    _sites[sk] = sk;
523                } else {
524                    sk = old;
525                }
526
527                sk.HitCount++;
528
529                CleanupNoLock();
530            }
531
532            return (T)sk.Site;
533        }
534
535        /// <summary>
536        /// Removes items from the cache that have the lowest usage...
537        /// </summary>
538        private void CleanupNoLock() {
539            // cleanup only if we have too many sites and we've created a bunch since our last cleanup
540            if (_sites.Count > CleanupThreshold && (LastCleanup < SitesCreated - CleanupThreshold)) {
541                LastCleanup = SitesCreated;
542
543                // calculate the average use, remove up to StopCleanupThreshold that are below average.
544                int totalUse = 0;
545                foreach (SiteKey sk in _sites.Keys) {
546                    totalUse += sk.HitCount;
547                }
548
549                int avgUse = totalUse / _sites.Count;
550                if (avgUse == 1 && _sites.Count > ClearThreshold) {
551                    // we only have a bunch of one-off requests
552                    _sites.Clear();
553                    return;
554                }
555
556                List<SiteKey> toRemove = null;
557                foreach (SiteKey sk in _sites.Keys) {
558                    if (sk.HitCount < (avgUse - RemoveThreshold)) {
559                        if (toRemove == null) {
560                            toRemove = new List<SiteKey>();
561                        }
562
563                        toRemove.Add(sk);
564                        if (toRemove.Count > StopCleanupThreshold) {
565                            // if we have a setup like weight(100), weight(1), weight(1), weight(1), ... we don't want
566                            // to just run through and remove all of the weight(1)'s. 
567                            break;
568                        }
569                    }
570
571                }
572
573                if (toRemove != null) {
574                    foreach (SiteKey sk in toRemove) {
575                        _sites.Remove(sk);
576                    }
577
578                    // reset all hit counts so the next time through is fair 
579                    // to newly added members which may take precedence.
580                    foreach (SiteKey sk in _sites.Keys) {
581                        sk.HitCount = 0;
582                    }
583                }
584            }
585        }
586
587        /// <summary>
588        /// Helper class for tracking all of our unique dynamic sites and their
589        /// usage patterns.  We hash on the combination of the binder and site type.
590        /// 
591        /// We also track the hit count and the key holds the site associated w/ the 
592        /// key.  Logically this is a set based upon the binder and site-type but we
593        /// store it in a dictionary.
594        /// </summary>
595        private class SiteKey : IEquatable<SiteKey> {
596            // the key portion of the data
597            internal readonly CallSiteBinder SiteBinder;
598            private readonly Type _siteType;
599
600            // not used for equality, used for caching strategy
601            public int HitCount;
602            public CallSite Site;
603
604            public SiteKey(Type siteType, CallSiteBinder siteBinder) {
605                Debug.Assert(siteType != null);
606                Debug.Assert(siteBinder != null);
607
608                SiteBinder = siteBinder;
609                _siteType = siteType;
610            }
611
612            [Confined]
613            public override bool Equals(object obj) {
614                return Equals(obj as SiteKey);
615            }
616
617            [Confined]
618            public override int GetHashCode() {
619                return SiteBinder.GetHashCode() ^ _siteType.GetHashCode();
620            }
621
622            #region IEquatable<SiteKey> Members
623
624            [StateIndependent]
625            public bool Equals(SiteKey other) {
626                if (other == null) return false;
627
628                return other.SiteBinder.Equals(SiteBinder) &&
629                    other._siteType == _siteType;
630            }
631
632            #endregion
633#if DEBUG
634            [Confined]
635            public override string ToString() {
636                return String.Format("{0} {1}", SiteBinder.ToString(), HitCount);
637            }
638#endif
639        }
640
641        private Func<DynamicOperations, CallSiteBinder, object, object[], object> GetInvoker(int paramCount) {
642            Func<DynamicOperations, CallSiteBinder, object, object[], object> invoker;
643            lock (_invokers) {
644                if (!_invokers.TryGetValue(paramCount, out invoker)) {
645                    ParameterExpression dynOps = Expression.Parameter(typeof(DynamicOperations));
646                    ParameterExpression callInfo = Expression.Parameter(typeof(CallSiteBinder));
647                    ParameterExpression target = Expression.Parameter(typeof(object));
648                    ParameterExpression args = Expression.Parameter(typeof(object[]));
649                    Type funcType = DelegateUtils.GetObjectCallSiteDelegateType(paramCount);
650                    ParameterExpression site = Expression.Parameter(typeof(CallSite<>).MakeGenericType(funcType));
651                    Expression[] siteArgs = new Expression[paramCount + 2];
652                    siteArgs[0] = site;
653                    siteArgs[1] = target;
654                    for (int i = 0; i < paramCount; i++) {
655                        siteArgs[i + 2] = Expression.ArrayIndex(args, Expression.Constant(i));
656                    }
657
658                    var getOrCreateSiteFunc = new Func<CallSiteBinder, CallSite<Func<object>>>(GetOrCreateSite<Func<object>>).Method.GetGenericMethodDefinition();
659                    _invokers[paramCount] = invoker = Expression.Lambda<Func<DynamicOperations, CallSiteBinder, object, object[], object>>(
660                        Expression.Block(
661                            new[] { site },
662                            Expression.Assign(
663                                site,
664                                Expression.Call(dynOps, getOrCreateSiteFunc.MakeGenericMethod(funcType), callInfo)
665                            ),
666                            Expression.Invoke(
667                                Expression.Field(
668                                    site,
669                                    site.Type.GetField("Target")
670                                ),
671                                siteArgs
672                            )
673                        ),
674                        new[] { dynOps, callInfo, target, args }
675                    ).Compile();
676                }
677            }
678            return invoker;
679        }
680       
681        #endregion
682    }
683}