EventTracker.cs :  » Script » IronRuby » Microsoft » Scripting » Actions » C# / CSharp Open Source

Home
C# / CSharp Open Source
1.2.6.4 mono .net core
2.2.6.4 mono core
3.Aspect Oriented Frameworks
4.Bloggers
5.Build Systems
6.Business Application
7.Charting Reporting Tools
8.Chat Servers
9.Code Coverage Tools
10.Content Management Systems CMS
11.CRM ERP
12.Database
13.Development
14.Email
15.Forum
16.Game
17.GIS
18.GUI
19.IDEs
20.Installers Generators
21.Inversion of Control Dependency Injection
22.Issue Tracking
23.Logging Tools
24.Message
25.Mobile
26.Network Clients
27.Network Servers
28.Office
29.PDF
30.Persistence Frameworks
31.Portals
32.Profilers
33.Project Management
34.RSS RDF
35.Rule Engines
36.Script
37.Search Engines
38.Sound Audio
39.Source Control
40.SQL Clients
41.Template Engines
42.Testing
43.UML
44.Web Frameworks
45.Web Service
46.Web Testing
47.Wiki Engines
48.Windows Presentation Foundation
49.Workflows
50.XML Parsers
C# / C Sharp
C# / C Sharp by API
C# / CSharp Tutorial
C# / CSharp Open Source » Script » IronRuby 
IronRuby » Microsoft » Scripting » Actions » EventTracker.cs
/* ****************************************************************************
 *
 * Copyright (c) Microsoft Corporation. 
 *
 * This source code is subject to terms and conditions of the Microsoft Public License. A 
 * copy of the license can be found in the License.html file at the root of this distribution. If 
 * you cannot locate the  Microsoft Public License, please send an email to 
 * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound 
 * by the terms of the Microsoft Public License.
 *
 * You must not remove this notice, or any other, from this software.
 *
 *
 * ***************************************************************************/

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Dynamic;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Threading;

using Microsoft.Contracts;
using Microsoft.Scripting.Actions.Calls;
using Microsoft.Scripting.Generation;
using Microsoft.Scripting.Runtime;
using Microsoft.Scripting.Utils;

namespace Microsoft.Scripting.Actions{
    public class EventTracker : MemberTracker {
        // For each instance of the class that declares the event there is a list of pairs in a table 
        // (like if we added an instance field for each instance event into its declaring class). 
        // We use _staticTarget for a static event (it is bound to its declaring type).
        // Each pair in the list holds on the stub handler that was added to the event delegate chain and the callable 
        // object that is passed to +=/-= operators. 
        private WeakDictionary<object, NormalHandlerList> _handlerLists;
        private static readonly object _staticTarget = new object();

        private readonly EventInfo _eventInfo;
        private MethodInfo _addMethod;
        private MethodInfo _removeMethod;

        internal EventTracker(EventInfo eventInfo) {
            Assert.NotNull(eventInfo);
            _eventInfo = eventInfo;
        }

        public override Type DeclaringType {
            get { return _eventInfo.DeclaringType; }
        }

        public override TrackerTypes MemberType {
            get { return TrackerTypes.Event; }
        }

        public override string Name {
            get { return _eventInfo.Name; }
        }

        public EventInfo Event {
            get {
                return _eventInfo;
            }
        }

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")]
        public MethodInfo GetCallableAddMethod() {
            if (_addMethod == null) {
                _addMethod = CompilerHelpers.TryGetCallableMethod(_eventInfo.GetAddMethod(true));
            }
            return _addMethod;
        }

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")]
        public MethodInfo GetCallableRemoveMethod() {
            if (_removeMethod == null) {
                _removeMethod = CompilerHelpers.TryGetCallableMethod(_eventInfo.GetRemoveMethod(true));
            }
            return _removeMethod;
        }

        /// <summary>
        /// Doesn't need to check PrivateBinding setting: no method that is part of the event is public the entire event is private. 
        /// If the code has already a reference to the event tracker instance for a private event its "static-ness" is not influenced 
        /// by private-binding setting.
        /// </summary>
        public bool IsStatic {
            get {
                MethodInfo mi = Event.GetAddMethod(false) ??
                    Event.GetRemoveMethod(false) ??
                    Event.GetRaiseMethod(false) ??
                    Event.GetAddMethod(true) ??
                    Event.GetRemoveMethod(true) ??
                    Event.GetRaiseMethod(true);

                MethodInfo m;
                Debug.Assert(
                    ((m = Event.GetAddMethod(true)) == null || m.IsStatic == mi.IsStatic) &&
                    ((m = Event.GetRaiseMethod(true)) == null || m.IsStatic == mi.IsStatic) &&
                    ((m = Event.GetRaiseMethod(true)) == null || m.IsStatic == mi.IsStatic),
                    "Methods are either all static or all instance."
                );
                
                return mi.IsStatic;
            }
        }

        protected internal override DynamicMetaObject GetBoundValue(OverloadResolverFactory resolverFactory, ActionBinder binder, Type type, DynamicMetaObject instance) {
            return binder.ReturnMemberTracker(type, new BoundMemberTracker(this, instance));
        }

        public override MemberTracker BindToInstance(DynamicMetaObject instance) {
            if (IsStatic) {
                return this;
            }

            return new BoundMemberTracker(this, instance);
        }

        [Confined]
        public override string ToString() {
            return _eventInfo.ToString();
        }

        public void AddHandler(object target, object handler, DynamicDelegateCreator delegateCreator) {
            ContractUtils.RequiresNotNull(handler, "handler");
            ContractUtils.RequiresNotNull(delegateCreator, "delegateCreator");

            Delegate delegateHandler;
            HandlerList stubs;

            // we can add event directly (signature does match):
            if (_eventInfo.EventHandlerType.IsAssignableFrom(handler.GetType())) {
                delegateHandler = (Delegate)handler;
                stubs = null;
            } else {
                // create signature converting stub:
                delegateHandler = delegateCreator.GetDelegate(handler, _eventInfo.EventHandlerType);
                stubs = GetHandlerList(target);
            }

            GetCallableAddMethod().Invoke(target, new object[] { delegateHandler });

            if (stubs != null) {
                // remember the stub so that we could search for it on removal:
                stubs.AddHandler(handler, delegateHandler);
            }
        }

        public void RemoveHandler(object target, object handler, IEqualityComparer<object> objectComparer) {
            ContractUtils.RequiresNotNull(handler, "handler");
            ContractUtils.RequiresNotNull(objectComparer, "objectComparer");

            Delegate delegateHandler;
            if (_eventInfo.EventHandlerType.IsAssignableFrom(handler.GetType())) {
                delegateHandler = (Delegate)handler;
            } else {
                delegateHandler = GetHandlerList(target).RemoveHandler(handler, objectComparer);
            }

            if (delegateHandler != null) {
                GetCallableRemoveMethod().Invoke(target, new object[] { delegateHandler });
            }
        }

        #region Private Implementation Details

        private HandlerList GetHandlerList(object instance) {
#if !SILVERLIGHT
            if (TypeUtils.IsComObject(instance)) {
                return GetComHandlerList(instance);
            }
#endif

            if (_handlerLists == null) {
                Interlocked.CompareExchange(ref _handlerLists, new WeakDictionary<object, NormalHandlerList>(), null);
            }

            if (instance == null) {
                // targetting a static method, we'll use a random object
                // as our place holder here...
                instance = _staticTarget;
            }

            lock (_handlerLists) {
                NormalHandlerList result;
                if (_handlerLists.TryGetValue(instance, out result)) {
                    return result;
                }

                result = new NormalHandlerList();
                _handlerLists[instance] = result;
                return result;
            }
        }

#if !SILVERLIGHT
        /// <summary>
        /// Gets the stub list for a COM Object.  For COM objects we store the stub list
        /// directly on the object using the Marshal APIs.  This allows us to not have
        /// any circular references to deal with via weak references which are challenging
        /// in the face of COM.
        /// </summary>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2201:DoNotRaiseReservedExceptionTypes")]
        private HandlerList GetComHandlerList(object instance) {
            HandlerList hl = (HandlerList)Marshal.GetComObjectData(instance, this);
            if (hl == null) {
                lock (_staticTarget) {
                    hl = (HandlerList)Marshal.GetComObjectData(instance, this);
                    if (hl == null) {
                        hl = new ComHandlerList();
                        if (!Marshal.SetComObjectData(instance, this, hl)) {
                            throw new COMException("Failed to set COM Object Data");
                        }
                    }
                }
            }

            return hl;
        }
#endif

        /// <summary>
        /// Holds on a list of delegates hooked to the event. 
        /// We need the list because we cannot enumerate the delegates hooked to CLR event and we need to do so in 
        /// handler removal (we need to do custom delegate comparison there). If BCL enables the enumeration we could remove this.
        /// </summary>
        private abstract class HandlerList {
            public abstract void AddHandler(object callableObject, Delegate handler);
            public abstract Delegate RemoveHandler(object callableObject, IEqualityComparer<object> comparer);
        }

#if !SILVERLIGHT
        private sealed class ComHandlerList : HandlerList {
            /// <summary>
            /// Storage for the handlers - a key value pair of the callable object and the delegate handler.
            /// </summary>
            private readonly CopyOnWriteList<KeyValuePair<object, object>> _handlers = new CopyOnWriteList<KeyValuePair<object, object>>();

            public override void AddHandler(object callableObject, Delegate handler) {
                Assert.NotNull(handler);
                _handlers.Add(new KeyValuePair<object, object>(callableObject, handler));
            }

            public override Delegate RemoveHandler(object callableObject, IEqualityComparer<object> comparer) {
                List<KeyValuePair<object, object>> copyOfHandlers = _handlers.GetCopyForRead();
                for (int i = copyOfHandlers.Count - 1; i >= 0; i--) {
                    object key = copyOfHandlers[i].Key;
                    object value = copyOfHandlers[i].Value;

                    if (comparer.Equals(key, callableObject)) {
                        Delegate handler = (Delegate)value;
                        _handlers.RemoveAt(i);
                        return handler;
                    }
                }

                return null;
            }
        }
#endif

        private sealed class NormalHandlerList : HandlerList {
            /// <summary>
            /// Storage for the handlers - a key value pair of the callable object and the delegate handler.
            /// 
            /// The delegate handler is closed over the callable object.  Therefore as long as the object is alive the
            /// delegate will stay alive and so will the callable object.  That means it's fine to have a weak reference
            /// to both of these objects.
            /// </summary>
            private readonly CopyOnWriteList<KeyValuePair<WeakReference, WeakReference>> _handlers = new CopyOnWriteList<KeyValuePair<WeakReference, WeakReference>>();

            public NormalHandlerList() {
            }

            public override void AddHandler(object callableObject, Delegate handler) {
                Assert.NotNull(handler);
                _handlers.Add(new KeyValuePair<WeakReference, WeakReference>(new WeakReference(callableObject), new WeakReference(handler)));
            }

            public override Delegate RemoveHandler(object callableObject, IEqualityComparer<object> comparer) {
                List<KeyValuePair<WeakReference, WeakReference>> copyOfHandlers = _handlers.GetCopyForRead();
                for (int i = copyOfHandlers.Count - 1; i >= 0; i--) {
                    object key = copyOfHandlers[i].Key.Target;
                    object value = copyOfHandlers[i].Value.Target;

                    if (key != null && value != null && comparer.Equals(key, callableObject)) {
                        Delegate handler = (Delegate)value;
                        _handlers.RemoveAt(i);
                        return handler;
                    }
                }

                return null;
            }
        }

        #endregion
    }
}
www.java2v.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.