PageRenderTime 49ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/Microsoft.Scripting/Runtime/DynamicOperations.cs

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