PageRenderTime 46ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 1ms

/Runtime/Microsoft.Dynamic/Actions/EventTracker.cs

http://github.com/IronLanguages/main
C# | 306 lines | 206 code | 49 blank | 51 comment | 43 complexity | 7008f2e0e1b11d298ab6b09c7cd50146 MD5 | raw file
Possible License(s): CPL-1.0, BSD-3-Clause, ISC, GPL-2.0, MPL-2.0-no-copyleft-exception
  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. using System;
  16. using System.Collections.Generic;
  17. using System.Diagnostics;
  18. using System.Dynamic;
  19. using System.Reflection;
  20. using System.Runtime.InteropServices;
  21. using System.Threading;
  22. using Microsoft.Scripting.Actions.Calls;
  23. using Microsoft.Scripting.Generation;
  24. using Microsoft.Scripting.Runtime;
  25. using Microsoft.Scripting.Utils;
  26. namespace Microsoft.Scripting.Actions {
  27. public class EventTracker : MemberTracker {
  28. // For each instance of the class that declares the event there is a list of pairs in a table
  29. // (like if we added an instance field for each instance event into its declaring class).
  30. // We use _staticTarget for a static event (it is bound to its declaring type).
  31. // Each pair in the list holds on the stub handler that was added to the event delegate chain and the callable
  32. // object that is passed to +=/-= operators.
  33. private WeakDictionary<object, NormalHandlerList> _handlerLists;
  34. private static readonly object _staticTarget = new object();
  35. private readonly EventInfo _eventInfo;
  36. private MethodInfo _addMethod;
  37. private MethodInfo _removeMethod;
  38. internal EventTracker(EventInfo eventInfo) {
  39. Assert.NotNull(eventInfo);
  40. _eventInfo = eventInfo;
  41. }
  42. public override Type DeclaringType {
  43. get { return _eventInfo.DeclaringType; }
  44. }
  45. public override TrackerTypes MemberType {
  46. get { return TrackerTypes.Event; }
  47. }
  48. public override string Name {
  49. get { return _eventInfo.Name; }
  50. }
  51. public EventInfo Event {
  52. get {
  53. return _eventInfo;
  54. }
  55. }
  56. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")]
  57. public MethodInfo GetCallableAddMethod() {
  58. if (_addMethod == null) {
  59. _addMethod = _eventInfo.GetAddMethod(true);
  60. }
  61. return _addMethod;
  62. }
  63. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")]
  64. public MethodInfo GetCallableRemoveMethod() {
  65. if (_removeMethod == null) {
  66. _removeMethod = _eventInfo.GetRemoveMethod(true);
  67. }
  68. return _removeMethod;
  69. }
  70. /// <summary>
  71. /// Doesn't need to check PrivateBinding setting: no method that is part of the event is public the entire event is private.
  72. /// If the code has already a reference to the event tracker instance for a private event its "static-ness" is not influenced
  73. /// by private-binding setting.
  74. /// </summary>
  75. public bool IsStatic {
  76. get {
  77. MethodInfo mi = Event.GetAddMethod(false) ??
  78. Event.GetRemoveMethod(false) ??
  79. Event.GetRaiseMethod(false) ??
  80. Event.GetAddMethod(true) ??
  81. Event.GetRemoveMethod(true) ??
  82. Event.GetRaiseMethod(true);
  83. MethodInfo m;
  84. Debug.Assert(
  85. ((m = Event.GetAddMethod(true)) == null || m.IsStatic == mi.IsStatic) &&
  86. ((m = Event.GetRaiseMethod(true)) == null || m.IsStatic == mi.IsStatic) &&
  87. ((m = Event.GetRaiseMethod(true)) == null || m.IsStatic == mi.IsStatic),
  88. "Methods are either all static or all instance."
  89. );
  90. return mi.IsStatic;
  91. }
  92. }
  93. protected internal override DynamicMetaObject GetBoundValue(OverloadResolverFactory resolverFactory, ActionBinder binder, Type type, DynamicMetaObject instance) {
  94. return binder.ReturnMemberTracker(type, new BoundMemberTracker(this, instance));
  95. }
  96. public override MemberTracker BindToInstance(DynamicMetaObject instance) {
  97. if (IsStatic) {
  98. return this;
  99. }
  100. return new BoundMemberTracker(this, instance);
  101. }
  102. public override string ToString() {
  103. return _eventInfo.ToString();
  104. }
  105. public void AddHandler(object target, object handler, DynamicDelegateCreator delegateCreator) {
  106. ContractUtils.RequiresNotNull(handler, "handler");
  107. ContractUtils.RequiresNotNull(delegateCreator, "delegateCreator");
  108. Delegate delegateHandler;
  109. HandlerList stubs;
  110. // we can add event directly (signature does match):
  111. if (_eventInfo.EventHandlerType.IsAssignableFrom(handler.GetType())) {
  112. delegateHandler = (Delegate)handler;
  113. stubs = null;
  114. } else {
  115. // create signature converting stub:
  116. delegateHandler = delegateCreator.GetDelegate(handler, _eventInfo.EventHandlerType);
  117. stubs = GetHandlerList(target);
  118. }
  119. var add = GetCallableAddMethod();
  120. // TODO (tomat): this used to use event.ReflectedType, is it still correct?
  121. if (target != null) {
  122. add = CompilerHelpers.TryGetCallableMethod(target.GetType(), add);
  123. }
  124. add.Invoke(target, new object[] { delegateHandler });
  125. if (stubs != null) {
  126. // remember the stub so that we could search for it on removal:
  127. stubs.AddHandler(handler, delegateHandler);
  128. }
  129. }
  130. public void RemoveHandler(object target, object handler, IEqualityComparer<object> objectComparer) {
  131. ContractUtils.RequiresNotNull(handler, "handler");
  132. ContractUtils.RequiresNotNull(objectComparer, "objectComparer");
  133. Delegate delegateHandler;
  134. if (_eventInfo.EventHandlerType.IsAssignableFrom(handler.GetType())) {
  135. delegateHandler = (Delegate)handler;
  136. } else {
  137. delegateHandler = GetHandlerList(target).RemoveHandler(handler, objectComparer);
  138. }
  139. if (delegateHandler != null) {
  140. GetCallableRemoveMethod().Invoke(target, new object[] { delegateHandler });
  141. }
  142. }
  143. #region Private Implementation Details
  144. private HandlerList GetHandlerList(object instance) {
  145. #if FEATURE_COM
  146. if (TypeUtils.IsComObject(instance)) {
  147. return GetComHandlerList(instance);
  148. }
  149. #endif
  150. if (_handlerLists == null) {
  151. Interlocked.CompareExchange(ref _handlerLists, new WeakDictionary<object, NormalHandlerList>(), null);
  152. }
  153. if (instance == null) {
  154. // targetting a static method, we'll use a random object
  155. // as our place holder here...
  156. instance = _staticTarget;
  157. }
  158. lock (_handlerLists) {
  159. NormalHandlerList result;
  160. if (_handlerLists.TryGetValue(instance, out result)) {
  161. return result;
  162. }
  163. result = new NormalHandlerList();
  164. _handlerLists[instance] = result;
  165. return result;
  166. }
  167. }
  168. #if FEATURE_COM
  169. /// <summary>
  170. /// Gets the stub list for a COM Object. For COM objects we store the stub list
  171. /// directly on the object using the Marshal APIs. This allows us to not have
  172. /// any circular references to deal with via weak references which are challenging
  173. /// in the face of COM.
  174. /// </summary>
  175. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2201:DoNotRaiseReservedExceptionTypes")]
  176. private HandlerList GetComHandlerList(object instance) {
  177. HandlerList hl = (HandlerList)Marshal.GetComObjectData(instance, this);
  178. if (hl == null) {
  179. lock (_staticTarget) {
  180. hl = (HandlerList)Marshal.GetComObjectData(instance, this);
  181. if (hl == null) {
  182. hl = new ComHandlerList();
  183. if (!Marshal.SetComObjectData(instance, this, hl)) {
  184. throw new COMException("Failed to set COM Object Data");
  185. }
  186. }
  187. }
  188. }
  189. return hl;
  190. }
  191. #endif
  192. /// <summary>
  193. /// Holds on a list of delegates hooked to the event.
  194. /// We need the list because we cannot enumerate the delegates hooked to CLR event and we need to do so in
  195. /// handler removal (we need to do custom delegate comparison there). If BCL enables the enumeration we could remove this.
  196. /// </summary>
  197. private abstract class HandlerList {
  198. public abstract void AddHandler(object callableObject, Delegate handler);
  199. public abstract Delegate RemoveHandler(object callableObject, IEqualityComparer<object> comparer);
  200. }
  201. #if !SILVERLIGHT
  202. private sealed class ComHandlerList : HandlerList {
  203. /// <summary>
  204. /// Storage for the handlers - a key value pair of the callable object and the delegate handler.
  205. /// </summary>
  206. private readonly CopyOnWriteList<KeyValuePair<object, object>> _handlers = new CopyOnWriteList<KeyValuePair<object, object>>();
  207. public override void AddHandler(object callableObject, Delegate handler) {
  208. Assert.NotNull(handler);
  209. _handlers.Add(new KeyValuePair<object, object>(callableObject, handler));
  210. }
  211. public override Delegate RemoveHandler(object callableObject, IEqualityComparer<object> comparer) {
  212. List<KeyValuePair<object, object>> copyOfHandlers = _handlers.GetCopyForRead();
  213. for (int i = copyOfHandlers.Count - 1; i >= 0; i--) {
  214. object key = copyOfHandlers[i].Key;
  215. object value = copyOfHandlers[i].Value;
  216. if (comparer.Equals(key, callableObject)) {
  217. Delegate handler = (Delegate)value;
  218. _handlers.RemoveAt(i);
  219. return handler;
  220. }
  221. }
  222. return null;
  223. }
  224. }
  225. #endif
  226. private sealed class NormalHandlerList : HandlerList {
  227. /// <summary>
  228. /// Storage for the handlers - a key value pair of the callable object and the delegate handler.
  229. ///
  230. /// The delegate handler is closed over the callable object. Therefore as long as the object is alive the
  231. /// delegate will stay alive and so will the callable object. That means it's fine to have a weak reference
  232. /// to both of these objects.
  233. /// </summary>
  234. private readonly CopyOnWriteList<KeyValuePair<WeakReference, WeakReference>> _handlers = new CopyOnWriteList<KeyValuePair<WeakReference, WeakReference>>();
  235. public NormalHandlerList() {
  236. }
  237. public override void AddHandler(object callableObject, Delegate handler) {
  238. Assert.NotNull(handler);
  239. _handlers.Add(new KeyValuePair<WeakReference, WeakReference>(new WeakReference(callableObject), new WeakReference(handler)));
  240. }
  241. public override Delegate RemoveHandler(object callableObject, IEqualityComparer<object> comparer) {
  242. List<KeyValuePair<WeakReference, WeakReference>> copyOfHandlers = _handlers.GetCopyForRead();
  243. for (int i = copyOfHandlers.Count - 1; i >= 0; i--) {
  244. object key = copyOfHandlers[i].Key.Target;
  245. object value = copyOfHandlers[i].Value.Target;
  246. if (key != null && value != null && comparer.Equals(key, callableObject)) {
  247. Delegate handler = (Delegate)value;
  248. _handlers.RemoveAt(i);
  249. return handler;
  250. }
  251. }
  252. return null;
  253. }
  254. }
  255. #endregion
  256. }
  257. }