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