// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. using System; using System.Reflection; using Microsoft.ClearScript.Util; namespace Microsoft.ClearScript { /// /// Provides the base implementation for a host event source. /// public abstract class EventSource { internal EventSource(ScriptEngine engine, object source, EventInfo eventInfo) { MiscHelpers.VerifyNonNullArgument(engine, nameof(engine)); MiscHelpers.VerifyNonNullArgument(eventInfo, nameof(eventInfo)); Engine = engine; Source = source; EventInfo = eventInfo; } internal ScriptEngine Engine { get; } internal abstract Type HandlerType { get; } internal object Source { get; } internal EventInfo EventInfo { get; } #region script-callable interface // ReSharper disable InconsistentNaming /// /// Connects the host event source to the specified script handler function. /// /// The script function that will handle the event. /// An that represents the connection. public EventConnection connect(object scriptFunc) { MiscHelpers.VerifyNonNullArgument(scriptFunc, nameof(scriptFunc)); return Engine.CreateEventConnection(HandlerType, Source, EventInfo, DelegateFactory.CreateDelegate(Engine, scriptFunc, HandlerType)); } // ReSharper restore InconsistentNaming #endregion } /// /// Represents a host event source. /// /// The event handler delegate type. public sealed class EventSource : EventSource { internal EventSource(ScriptEngine engine, object source, EventInfo eventInfo) : base(engine, source, eventInfo) { if (eventInfo.EventHandlerType != typeof(T)) { throw new ArgumentException("Invalid event type (handler type mismatch)", nameof(eventInfo)); } } #region EventSource overrides internal override Type HandlerType => typeof(T); #endregion #region script-callable interface // ReSharper disable InconsistentNaming /// /// Connects the host event source to the specified script handler function. /// /// The script function that will handle the event. /// An that represents the connection. public new EventConnection connect(object scriptFunc) { MiscHelpers.VerifyNonNullArgument(scriptFunc, nameof(scriptFunc)); return Engine.CreateEventConnection(Source, EventInfo, DelegateFactory.CreateDelegate(Engine, scriptFunc, typeof(T))); } // ReSharper restore InconsistentNaming #endregion } /// /// Provides the base implementation for a connection between a host event source and a script handler function. /// public abstract class EventConnection { private readonly ScriptEngine engine; private readonly object source; private readonly MethodInfo removeMethod; private readonly object[] parameters; private readonly InterlockedOneWayFlag brokenFlag = new(); internal EventConnection(ScriptEngine engine, object source, EventInfo eventInfo, Delegate handler) { MiscHelpers.VerifyNonNullArgument(engine, nameof(engine)); MiscHelpers.VerifyNonNullArgument(handler, nameof(handler)); MiscHelpers.VerifyNonNullArgument(eventInfo, nameof(eventInfo)); if (!MiscHelpers.Try(out var addMethod, static eventInfo => eventInfo.GetAddMethod(true), eventInfo) || (addMethod is null)) { throw new ArgumentException("Invalid event type (no accessible add method)", nameof(eventInfo)); } if (!MiscHelpers.Try(out removeMethod, static eventInfo => eventInfo.GetRemoveMethod(true), eventInfo) || (removeMethod is null)) { throw new ArgumentException("Invalid event type (no accessible remove method)", nameof(eventInfo)); } this.engine = engine; this.source = source; addMethod.Invoke(source, parameters = new object[] { handler }); } internal void Break() { if (brokenFlag.Set()) { removeMethod.Invoke(source, parameters); } } #region script-callable interface // ReSharper disable InconsistentNaming /// /// Disconnects the host event source from the script handler function. /// public void disconnect() { engine.BreakEventConnection(this); } // ReSharper restore InconsistentNaming #endregion } /// /// Represents a connection between a host event source and a script handler function. /// /// The event handler delegate type. public sealed class EventConnection : EventConnection { internal EventConnection(ScriptEngine engine, object source, EventInfo eventInfo, Delegate handler) : base(engine, source, eventInfo, handler) { if (eventInfo.EventHandlerType != typeof(T)) { throw new ArgumentException("Invalid event type (handler type mismatch)", nameof(eventInfo)); } } } }