PageRenderTime 48ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 1ms

/Microsoft.Scripting/Runtime/DynamicEventInfo.cs

https://bitbucket.org/stefanrusek/xronos
C# | 448 lines | 308 code | 81 blank | 59 comment | 64 complexity | 5e393364e7f9fb98feab448e473f2552 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.Reflection;
  22. using System.Runtime.CompilerServices;
  23. #if !CODEPLEX_40
  24. using Microsoft.Runtime.CompilerServices;
  25. #endif
  26. using System.Runtime.InteropServices;
  27. #if CODEPLEX_40
  28. using System.Dynamic;
  29. #else
  30. #endif
  31. using IronPython.Runtime.Operations;
  32. using Microsoft.Scripting;
  33. using Microsoft.Scripting.Generation;
  34. using Microsoft.Scripting.Runtime;
  35. using Microsoft.Scripting.Utils;
  36. namespace IronPython.Runtime.Types {
  37. /// <summary>
  38. /// The unbound representation of an event property
  39. /// </summary>
  40. [PythonType("event#")]
  41. public sealed class ReflectedEvent : PythonTypeSlot, ICodeFormattable {
  42. private readonly bool _clsOnly;
  43. private readonly EventInfo/*!*/ _eventInfo;
  44. private WeakDictionary<object, NormalHandlerList/*!*/> _handlerLists;
  45. private static readonly object _staticTarget = new object();
  46. internal ReflectedEvent(EventInfo/*!*/ eventInfo, bool clsOnly) {
  47. Assert.NotNull(eventInfo);
  48. _clsOnly = clsOnly;
  49. _eventInfo = eventInfo;
  50. }
  51. #region Internal APIs
  52. internal override bool TryGetValue(CodeContext/*!*/ context, object instance, PythonType owner, out object value) {
  53. Assert.NotNull(context, owner);
  54. value = new BoundEvent(this, instance, owner);
  55. return true;
  56. }
  57. internal override bool GetAlwaysSucceeds {
  58. get {
  59. return true;
  60. }
  61. }
  62. internal override bool TrySetValue(CodeContext/*!*/ context, object instance, PythonType owner, object value) {
  63. Assert.NotNull(context);
  64. BoundEvent et = value as BoundEvent;
  65. if (et == null || EventInfosDiffer(et)) {
  66. BadEventChange bea = value as BadEventChange;
  67. if (bea != null) {
  68. PythonType dt = bea.Owner as PythonType;
  69. if (dt != null) {
  70. if (bea.Instance == null) {
  71. throw new MissingMemberException(String.Format("attribute '{1}' of '{0}' object is read-only", dt.Name, SymbolTable.StringToId(_eventInfo.Name)));
  72. } else {
  73. throw new MissingMemberException(String.Format("'{0}' object has no attribute '{1}'", dt.Name, SymbolTable.StringToId(_eventInfo.Name)));
  74. }
  75. }
  76. }
  77. throw ReadOnlyException(DynamicHelpers.GetPythonTypeFromType(Info.DeclaringType));
  78. }
  79. return true;
  80. }
  81. private bool EventInfosDiffer(BoundEvent et) {
  82. // if they're the same object they're the same...
  83. if (et.Event.Info == this.Info) {
  84. return false;
  85. }
  86. // otherwise compare based upon type & metadata token (they
  87. // differ by ReflectedType)
  88. if (et.Event.Info.DeclaringType != Info.DeclaringType ||
  89. et.Event.Info.MetadataToken != Info.MetadataToken) {
  90. return true;
  91. }
  92. return false;
  93. }
  94. internal override bool IsSetDescriptor(CodeContext/*!*/ context, PythonType owner) {
  95. return true;
  96. }
  97. internal override bool TryDeleteValue(CodeContext/*!*/ context, object instance, PythonType owner) {
  98. Assert.NotNull(context, owner);
  99. throw ReadOnlyException(DynamicHelpers.GetPythonTypeFromType(Info.DeclaringType));
  100. }
  101. internal override bool IsAlwaysVisible {
  102. get {
  103. return !_clsOnly;
  104. }
  105. }
  106. private HandlerList/*!*/ GetStubList(object instance) {
  107. #if !SILVERLIGHT
  108. if (instance != null && ComOps.IsComObject(instance)) {
  109. return GetComStubList(instance);
  110. }
  111. #endif
  112. if (_handlerLists == null) {
  113. System.Threading.Interlocked.CompareExchange(ref _handlerLists, new WeakDictionary<object, NormalHandlerList>(), null);
  114. }
  115. if (instance == null) {
  116. // targetting a static method, we'll use a random object
  117. // as our place holder here...
  118. instance = _staticTarget;
  119. }
  120. lock (_handlerLists) {
  121. NormalHandlerList result;
  122. if (_handlerLists.TryGetValue(instance, out result)) {
  123. return result;
  124. }
  125. result = new NormalHandlerList();
  126. _handlerLists[instance] = result;
  127. return result;
  128. }
  129. }
  130. #if !SILVERLIGHT
  131. /// <summary>
  132. /// Gets the stub list for a COM Object. For COM objects we store the stub list
  133. /// directly on the object using the Marshal APIs. This allows us to not have
  134. /// any circular references to deal with via weak references which are challenging
  135. /// in the face of COM.
  136. /// </summary>
  137. private HandlerList/*!*/ GetComStubList(object/*!*/ instance) {
  138. HandlerList hl = (HandlerList)Marshal.GetComObjectData(instance, this);
  139. if (hl == null) {
  140. lock (_staticTarget) {
  141. hl = (HandlerList)Marshal.GetComObjectData(instance, this);
  142. if (hl == null) {
  143. hl = new ComHandlerList();
  144. if (!Marshal.SetComObjectData(instance, this, hl)) {
  145. throw new COMException("Failed to set COM Object Data");
  146. }
  147. }
  148. }
  149. }
  150. return hl;
  151. }
  152. #endif
  153. #endregion
  154. #region Public Python APIs
  155. public EventInfo/*!*/ Info {
  156. [PythonHidden]
  157. get {
  158. return _eventInfo;
  159. }
  160. }
  161. /// <summary>
  162. /// BoundEvent is the object that gets returned when the user gets an event object. An
  163. /// BoundEvent tracks where the event was received from and is used to verify we get
  164. /// a proper add when dealing w/ statics events.
  165. /// </summary>
  166. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible")] // TODO: fix
  167. public class BoundEvent {
  168. private readonly ReflectedEvent/*!*/ _event;
  169. private readonly PythonType/*!*/ _ownerType;
  170. private readonly object _instance;
  171. public ReflectedEvent/*!*/ Event {
  172. get {
  173. return _event;
  174. }
  175. }
  176. public BoundEvent(ReflectedEvent/*!*/ reflectedEvent, object instance, PythonType/*!*/ ownerType) {
  177. Assert.NotNull(reflectedEvent, ownerType);
  178. _event = reflectedEvent;
  179. _instance = instance;
  180. _ownerType = ownerType;
  181. }
  182. // this one's correct, InPlaceAdd is wrong but we still have some dependencies on the wrong name.
  183. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2225:OperatorOverloadsHaveNamedAlternates")] // TODO: fix
  184. [SpecialName]
  185. public object op_AdditionAssignment(CodeContext/*!*/ context, object func) {
  186. return InPlaceAdd(context, func);
  187. }
  188. [SpecialName]
  189. public object InPlaceAdd(CodeContext/*!*/ context, object func) {
  190. if (func == null) {
  191. throw PythonOps.TypeError("event addition expected callable object, got None");
  192. }
  193. MethodInfo add = _event.Info.GetAddMethod(true);
  194. if (add.IsStatic) {
  195. if (_ownerType != DynamicHelpers.GetPythonTypeFromType(_event.Info.DeclaringType)) {
  196. // mutating static event, only allow this from the type we're mutating, not sub-types
  197. return new BadEventChange(_ownerType, _instance);
  198. }
  199. }
  200. Delegate handler;
  201. HandlerList stubs;
  202. // we can add event directly (signature does match):
  203. if (_event.Info.EventHandlerType.IsAssignableFrom(func.GetType())) {
  204. handler = (Delegate)func;
  205. stubs = null;
  206. } else {
  207. // create signature converting stub:
  208. handler = BinderOps.GetDelegate(context.LanguageContext, func, _event.Info.EventHandlerType);
  209. stubs = _event.GetStubList(_instance);
  210. }
  211. bool privateBinding = context.LanguageContext.DomainManager.Configuration.PrivateBinding;
  212. // wire the handler up:
  213. if (!add.DeclaringType.IsPublic) {
  214. add = CompilerHelpers.GetCallableMethod(add, privateBinding);
  215. }
  216. if ((add.IsPublic && add.DeclaringType.IsPublic) || privateBinding) {
  217. add.Invoke(_instance, new object[] { handler });
  218. } else {
  219. throw new ArgumentTypeException("cannot add to private event");
  220. }
  221. if (stubs != null) {
  222. // remember the stub so that we could search for it on removal:
  223. stubs.AddHandler(func, handler);
  224. }
  225. return this;
  226. }
  227. [SpecialName]
  228. public object InPlaceSubtract(CodeContext/*!*/ context, object func) {
  229. Assert.NotNull(context);
  230. if (func == null) {
  231. throw PythonOps.TypeError("event subtraction expected callable object, got None");
  232. }
  233. MethodInfo remove = _event.Info.GetRemoveMethod(true);
  234. if (remove.IsStatic) {
  235. if (_ownerType != DynamicHelpers.GetPythonTypeFromType(_event.Info.DeclaringType)) {
  236. // mutating static event, only allow this from the type we're mutating, not sub-types
  237. return new BadEventChange(_ownerType, _instance);
  238. }
  239. }
  240. bool privateBinding = context.LanguageContext.DomainManager.Configuration.PrivateBinding;
  241. if (!remove.DeclaringType.IsPublic) {
  242. remove = CompilerHelpers.GetCallableMethod(remove, privateBinding);
  243. }
  244. bool isRemovePublic = remove.IsPublic && remove.DeclaringType.IsPublic;
  245. if (isRemovePublic || privateBinding) {
  246. Delegate handler;
  247. if (_event.Info.EventHandlerType.IsAssignableFrom(func.GetType())) {
  248. handler = (Delegate)func;
  249. } else {
  250. handler = _event.GetStubList(_instance).RemoveHandler(context, func);
  251. }
  252. if (handler != null) {
  253. remove.Invoke(_instance, new object[] { handler });
  254. }
  255. } else {
  256. throw new ArgumentTypeException("cannot subtract from private event");
  257. }
  258. return this;
  259. }
  260. }
  261. public void __set__(CodeContext context, object instance, object value) {
  262. TrySetValue(context, instance, DynamicHelpers.GetPythonType(instance), value);
  263. }
  264. public new void __delete__(CodeContext context, object instance) {
  265. TryDeleteValue(context, instance, DynamicHelpers.GetPythonType(instance));
  266. }
  267. #endregion
  268. #region Private Helpers
  269. private class BadEventChange {
  270. private readonly PythonType/*!*/ _ownerType;
  271. private readonly object _instance;
  272. public BadEventChange(PythonType/*!*/ ownerType, object instance) {
  273. _ownerType = ownerType;
  274. _instance = instance;
  275. }
  276. public PythonType Owner {
  277. get {
  278. return _ownerType;
  279. }
  280. }
  281. public object Instance {
  282. get {
  283. return _instance;
  284. }
  285. }
  286. }
  287. /// <summary>
  288. /// Holds on a list of delegates hooked to the event.
  289. /// We need the list because we cannot enumerate the delegates hooked to CLR event and we need to do so in
  290. /// handler removal (we need to do custom delegate comparison there). If BCL enables the enumeration we could remove this.
  291. /// </summary>
  292. private abstract class HandlerList {
  293. public abstract void AddHandler(object callableObject, Delegate/*!*/ handler);
  294. public abstract Delegate RemoveHandler(CodeContext/*!*/ context, object callableObject);
  295. }
  296. #if !SILVERLIGHT
  297. private sealed class ComHandlerList : HandlerList {
  298. /// <summary>
  299. /// Storage for the handlers - a key value pair of the callable object and the delegate handler.
  300. ///
  301. /// The delegate handler is closed over the callable object. Therefore as long as the object is alive the
  302. /// delegate will stay alive and so will the callable object. That means it's fine to have a weak reference
  303. /// to both of these objects.
  304. /// </summary>
  305. private readonly CopyOnWriteList<KeyValuePair<object, object>> _handlers = new CopyOnWriteList<KeyValuePair<object, object>>();
  306. public override void AddHandler(object callableObject, Delegate/*!*/ handler) {
  307. Assert.NotNull(handler);
  308. _handlers.Add(new KeyValuePair<object, object>(callableObject, handler));
  309. }
  310. public override Delegate RemoveHandler(CodeContext context, object callableObject) {
  311. Assert.NotNull(context);
  312. List<KeyValuePair<object, object>> copyOfHandlers = _handlers.GetCopyForRead();
  313. for (int i = copyOfHandlers.Count - 1; i >= 0; i--) {
  314. object key = copyOfHandlers[i].Key;
  315. object value = copyOfHandlers[i].Value;
  316. if (PythonOps.EqualRetBool(context, key, callableObject)) {
  317. Delegate handler = (Delegate)value;
  318. _handlers.RemoveAt(i);
  319. return handler;
  320. }
  321. }
  322. return null;
  323. }
  324. }
  325. #endif
  326. private sealed class NormalHandlerList : HandlerList {
  327. /// <summary>
  328. /// Storage for the handlers - a key value pair of the callable object and the delegate handler.
  329. ///
  330. /// The delegate handler is closed over the callable object. Therefore as long as the object is alive the
  331. /// delegate will stay alive and so will the callable object. That means it's fine to have a weak reference
  332. /// to both of these objects.
  333. /// </summary>
  334. private readonly CopyOnWriteList<KeyValuePair<WeakReference/*!*/, WeakReference/*!*/>> _handlers = new CopyOnWriteList<KeyValuePair<WeakReference/*!*/, WeakReference/*!*/>>();
  335. public NormalHandlerList() {
  336. }
  337. public override void AddHandler(object callableObject, Delegate/*!*/ handler) {
  338. Assert.NotNull(handler);
  339. _handlers.Add(new KeyValuePair<WeakReference/*!*/, WeakReference/*!*/>(new WeakReference(callableObject), new WeakReference(handler)));
  340. }
  341. public override Delegate RemoveHandler(CodeContext/*!*/ context, object callableObject) {
  342. Assert.NotNull(context);
  343. List<KeyValuePair<WeakReference, WeakReference>> copyOfHandlers = _handlers.GetCopyForRead();
  344. for (int i = copyOfHandlers.Count - 1; i >= 0; i--) {
  345. object key = copyOfHandlers[i].Key.Target;
  346. object value = copyOfHandlers[i].Value.Target;
  347. if (key != null && value != null && PythonOps.EqualRetBool(context, key, callableObject)) {
  348. Delegate handler = (Delegate)value;
  349. _handlers.RemoveAt(i);
  350. return handler;
  351. }
  352. }
  353. return null;
  354. }
  355. }
  356. private MissingMemberException/*!*/ ReadOnlyException(PythonType/*!*/ dt) {
  357. Assert.NotNull(dt);
  358. return new MissingMemberException(String.Format("attribute '{1}' of '{0}' object is read-only", dt.Name, SymbolTable.StringToId(_eventInfo.Name)));
  359. }
  360. #endregion
  361. #region ICodeFormattable Members
  362. public string/*!*/ __repr__(CodeContext/*!*/ context) {
  363. return string.Format("<event# {0} on {1}>", Info.Name, Info.DeclaringType.Name);
  364. }
  365. #endregion
  366. }
  367. }