1
0
mirror of https://github.com/Ylianst/MeshAgent synced 2025-12-25 20:53:15 +00:00
Files
MeshAgent/microscript/ILibduktape_EventEmitter.c
2017-10-25 21:08:41 -07:00

810 lines
34 KiB
C

#if defined(WIN32) && !defined(_WIN32_WCE) && !defined(_MINCORE)
#define _CRTDBG_MAP_ALLOC
#include <crtdbg.h>
#endif
#include "duktape.h"
#include "ILibDuktape_EventEmitter.h"
#include "ILibDuktapeModSearch.h"
#include "ILibDuktape_Helpers.h"
#define ILibDuktape_EventEmitter_MaxEventNameLen 255
#define ILibDuktape_EventEmitter_Data "\xFF_EventEmitter_Data"
#define ILibDuktape_EventEmitter_TempObject "\xFF_EventEmitter_TempObject"
#define ILibDuktape_EventEmitter_DispatcherFunc "\xFF_EventEmitter_DispatcherFunc"
#define ILibDuktape_EventEmitter_SetterFunc ((void*)0xFFFF)
#define ILibDuktape_EventEmitter_HPTR_LIST "\xFF_EventEmitter_HPTR_LIST"
#define ILibDuktape_EventEmitter_Hook ((void*)0xEEEE)
#define ILibDuktape_EventEmitter_GlobalListenerCount "\xFF_EventEmitter_GlobalListenerCount"
#ifdef __DOXY__
/*!
\brief Asynchronous event-driven class, that periodically emit named events that cause Function objects ("listeners") to be called.
*/
class EventEmitter
{
public:
/*!
\brief Adds the listener function to the end of the listeners array for the event specified by eventName.
\param eventName \<String\> The name of the event to associate the listener with.
\param func The listener function to attach.
*/
void on(eventName, func);
/*!
\brief Adds a one time listener function for the event named by eventName. The next time the event is triggered, this listener is removed and then invoked.
\param eventName \<String\> The name of the event to associate the listener with.
\param func The listener function to attach.
*/
void once(eventName, func);
/*!
\brief Synchronously calls each of the listeners registered for the event named by eventName, in the order they were registered, passing the supplied arguments to each.
\param eventName \<String\> The named event whose registered listeners are to be dispatched
\param args <Any> The optional parameters that will be passed to the listener functions.
*/
void emit(eventName[, ...args]);
/*!
\brief Removes the specified listener from the listener array for the event named eventName.
\param eventName \<String\>
\param listener <func>
*/
void removeListener(eventName, listener);
/*!
\brief Removes all listeners, or those of the specified eventName. <b>Note:</b> It is bad practice to remove listeners added elsewhere in the code, particularly when the EventEmitter instance was created by some other component or module.
*
void removeAllListeners([eventName]);
\param eventName \<String\> Optional
*/
void removeAllListeners([eventName]);
/*!
\brief EventEmitter helper class. <b>Note:</b> To use, must <b>require('events')</b>
*/
class events
{
public:
/*!
\brief Adds EventEmitter methods and events to the supplied object
\param obj Normally, <b>'this'</b> object should be passed, so that EventEmitter can be added to it.
\return Returns an events instance object that can be used to add events and methods to the EventEmitter implementation that was integrated
*/
static events inherits(obj);
/*!
\brief Helper method, that will implement the necessary plumbing to expose a named event
*
void createEvent(name);
\param name \<String\> The named event to create
*/
void createEvent(name);
/*!
\brief Helper method, that will implement the necessary plumbing to expose an object instance method. Particularly useful if the method name is the same as a named event.
*
void addMethod(name, func);\n
The instance method will be implemented as a Property, in which the getter returns the supplied function.
\param name The name of the instance method to expose
\param func The function to dispatch when the method is called
*/
void addMethod(name, func);
};
};
#endif
ILibDuktape_EventEmitter* ILibDuktape_EventEmitter_GetEmitter_fromObject(duk_context *ctx, void *objHeapptr)
{
ILibDuktape_EventEmitter *retVal = NULL;
duk_push_heapptr(ctx, objHeapptr); // [obj]
retVal = ILibDuktape_EventEmitter_GetEmitter(ctx, -1);
duk_pop(ctx); // ...
return(retVal);
}
void ILibDuktape_EventEmitter_FinalizerEx(ILibHashtable sender, void *Key1, char* Key2, int Key2Len, void *Data, void *user)
{
ILibDuktape_EventEmitter *data = (ILibDuktape_EventEmitter*)user;
int count, i;
void **hptr;
if (Key1 == NULL)
{
// If this is NULL, then 'Data' is a LinkedList of JavaScript Subscribers
ILibLinkedList_Destroy(Data);
}
else if(Key1 == ILibDuktape_EventEmitter_SetterFunc)
{
// If this is not NULL, this is the JavaScript Setter Func
duk_push_heapptr(data->ctx, Data); // [Setter]
duk_get_prop_string(data->ctx, -1, ILibDuktape_EventEmitter_HPTR_LIST); // [Setter][list]
count = (int)duk_get_length(data->ctx, -1);
for (i = 0; i < count; ++i)
{
duk_get_prop_index(data->ctx, -1, i); // [Setter][list][pointer]
hptr = (void**)duk_get_pointer(data->ctx, -1);
if (hptr != NULL) { *hptr = NULL; }
duk_pop(data->ctx); // [Setter][list]
}
duk_pop_2(data->ctx); // ...
}
}
duk_ret_t ILibDuktape_EventEmitter_Finalizer(duk_context *ctx)
{
ILibDuktape_EventEmitter *data;
duk_get_prop_string(ctx, 0, ILibDuktape_EventEmitter_Data);
data = (ILibDuktape_EventEmitter*)Duktape_GetBuffer(ctx, -1, NULL);
// Check to see if this is the process object going away
if (ILibDuktape_GetProcessObject(ctx) == data->object)
{
// We need to dispatch the 'exit' event
int exitCode = 0;
duk_push_heapptr(data->ctx, data->object); // [process]
if (duk_has_prop_string(data->ctx, -1, "\xFF_ExitCode"))
{
duk_get_prop_string(data->ctx, -1, "\xFF_ExitCode"); // [process][exitCode]
exitCode = duk_get_int(data->ctx, -1);
duk_pop(data->ctx); // [process]
}
duk_get_prop_string(data->ctx, -1, "emit"); // [process][emit]
duk_swap_top(data->ctx, -2); // [emit][this]
duk_push_string(data->ctx, "exit"); // [emit][this][eventName/exit]
duk_push_int(data->ctx, exitCode); // [emit][this][eventName/exit][exitCode]
duk_pcall_method(data->ctx, 2);
duk_pop(data->ctx);
}
// We need to clear the Native Dispatcher, while destroying the Hashtable
ILibHashtable_DestroyEx(data->eventTable, ILibDuktape_EventEmitter_FinalizerEx, data);
memset(data, 0, sizeof(ILibDuktape_EventEmitter));
return 0;
}
duk_ret_t ILibDuktape_EventEmitter_emit(duk_context *ctx)
{
duk_size_t nameLen;
if (!duk_is_string(ctx, 0)) { return ILibDuktape_Error(ctx, "EventEmitter.emit(): Invalid Parameter Name/Type"); }
char *name = (char*)duk_get_lstring(ctx, 0, &nameLen);
ILibLinkedList eventList;
void *self;
int nargs = duk_get_top(ctx);
ILibDuktape_EventEmitter *data;
void *node, *nextNode, *func, *dispatcher;
int i, j, count;
void **hptr;
void **emitList;
duk_push_this(ctx);
duk_get_prop_string(ctx, -1, ILibDuktape_EventEmitter_TempObject); // [this][tmp]
duk_get_prop_string(ctx, -1, ILibDuktape_EventEmitter_Data); // [this][tmp][data]
data = (ILibDuktape_EventEmitter*)Duktape_GetBuffer(ctx, -1, NULL);
duk_pop_2(ctx); // [this]
self = duk_get_heapptr(ctx, -1);
if (data->eventTable == NULL) { return 0; } // This probably means the finalizer was already run on the eventEmitter
eventList = ILibHashtable_Get(data->eventTable, NULL, name, (int)nameLen);
if (eventList == NULL) { return ILibDuktape_Error(ctx, "EventEmitter.emit(): Event '%s' not found", name); }
dispatcher = ILibHashtable_Get(data->eventTable, ILibDuktape_EventEmitter_SetterFunc, name, (int)nameLen);
if (dispatcher == NULL) { return ILibDuktape_Error(ctx, "EventEmitter.emit(): Internal Error with event '%s'", name); }
// Copy the list, so we can enumerate with local memory, so the list can be manipulated while we are dispatching
#ifdef WIN32
emitList = (void**)_alloca(((unsigned int)ILibLinkedList_GetCount(eventList) + 1) * sizeof(void*));
#else
emitList = (void**)alloca(((unsigned int)ILibLinkedList_GetCount(eventList) + 1) * sizeof(void*));
#endif
node = ILibLinkedList_GetNode_Head(eventList);
i = 0;
while (node != NULL)
{
nextNode = ILibLinkedList_GetNextNode(node);
emitList[i++] = ILibLinkedList_GetDataFromNode(node);
if (((int*)ILibLinkedList_GetExtendedMemory(node))[0] == 1)
{
// Dispatch only Once
ILibLinkedList_Remove(node);
data->totalListeners[0]--;
}
node = nextNode;
}
emitList[i] = NULL;
// If no more listeners, we can set the hptr to NULL
if (ILibLinkedList_GetCount(eventList) == 0)
{
duk_push_heapptr(ctx, dispatcher); // [dispatcher]
duk_get_prop_string(ctx, -1, ILibDuktape_EventEmitter_HPTR_LIST); // [dispatcher][hptrList]
count = (int)duk_get_length(ctx, -1);
for (i = 0; i < count; ++i)
{
duk_get_prop_index(ctx, -1, i); // [dispatcher][hptrList][hptr]
hptr = (void**)duk_get_pointer(ctx, -1);
*hptr = NULL;
duk_pop(ctx); // [dispatcher][hptrList]
}
duk_pop_2(ctx); // ...
}
// Now that we have all the housekeeping stuff out of the way, we can actually dispatch our events
i = 0;
while ((func = emitList[i++]) != NULL)
{
duk_push_heapptr(ctx, func); // [func]
duk_push_heapptr(ctx, self); // [func][this]
for (j = 1; j < nargs; ++j)
{
duk_dup(ctx, j); // [func][this][...args...]
}
if (duk_pcall_method(ctx, nargs - 1) != 0)
{
return(ILibDuktape_Error(ctx, "EventEmitter.emit(): Event dispatch for '%s' threw an exception: %s", name, duk_safe_to_string(ctx, -1)));
}
duk_pop(ctx); // ...
}
return 0;
}
int ILibDuktape_EventEmitter_AddOnce(ILibDuktape_EventEmitter *emitter, char *eventName, void *heapptr)
{
int retVal = 1;
duk_push_heapptr(emitter->ctx, emitter->object); // [obj]
duk_get_prop_string(emitter->ctx, -1, "once"); // [obj][once/func]
duk_swap_top(emitter->ctx, -2); // [once/func][this]
duk_push_string(emitter->ctx, eventName); // [once/func][this][eventName]
duk_push_heapptr(emitter->ctx, heapptr); // [once/func][this][eventName][callback]
if (duk_pcall_method(emitter->ctx, 2) == 0) { retVal = 0; }
duk_pop(emitter->ctx); // ...
return retVal;
}
int ILibDuktape_EventEmitter_AddOn(ILibDuktape_EventEmitter *emitter, char *eventName, void *heapptr)
{
int retVal = 1;
duk_push_heapptr(emitter->ctx, emitter->object); // [obj]
duk_get_prop_string(emitter->ctx, -1, "on"); // [obj][once/func]
duk_swap_top(emitter->ctx, -2); // [once/func][this]
duk_push_string(emitter->ctx, eventName); // [once/func][this][eventName]
duk_push_heapptr(emitter->ctx, heapptr); // [once/func][this][eventName][callback]
if (duk_pcall_method(emitter->ctx, 2) == 0) { retVal = 0; }
duk_pop(emitter->ctx); // ...
return retVal;
}
duk_ret_t ILibDuktape_EventEmitter_on(duk_context *ctx)
{
duk_size_t propNameLen;
if (!duk_is_string(ctx, 0)) { return(ILibDuktape_Error(ctx, "EventEmitter.on(): Invalid Parameter Name/Type")); }
char *propName = (char*)duk_get_lstring(ctx, 0, &propNameLen);
void *callback = duk_require_heapptr(ctx, 1);
ILibDuktape_EventEmitter *data;
int once;
void *eventList, *node, *dispatcher, **hptr;
int i, count;
duk_push_current_function(ctx);
once = Duktape_GetIntPropertyValue(ctx, -1, "once", 0);
duk_push_this(ctx);
duk_get_prop_string(ctx, -1, ILibDuktape_EventEmitter_TempObject);
duk_get_prop_string(ctx, -1, ILibDuktape_EventEmitter_Data);
data = (ILibDuktape_EventEmitter*)Duktape_GetBuffer(ctx, -1, NULL);
eventList = ILibHashtable_Get(data->eventTable, NULL, propName, (int)propNameLen);
if (eventList == NULL)
{
return(ILibDuktape_Error(ctx, "EventEmitter.on(): Event '%s' not found", propName));
}
dispatcher = ILibHashtable_Get(data->eventTable, ILibDuktape_EventEmitter_SetterFunc, propName, (int)propNameLen);
if (dispatcher == NULL) { return(ILibDuktape_Error(ctx, "EventEmitter.on(): Internal error with Event '%s'", propName)); }
node = ILibLinkedList_AddTail(eventList, callback);
((int*)ILibLinkedList_GetExtendedMemory(node))[0] = once;
data->totalListeners[0]++;
duk_push_heapptr(ctx, data->tmpObject);
duk_push_heapptr(ctx, callback);
duk_put_prop_string(ctx, -2, Duktape_GetStashKey(callback)); // Save the callback to the tmp object, so it won't get GC'ed
duk_push_heapptr(ctx, dispatcher); // [dispatcher]
duk_get_prop_string(ctx, -1, ILibDuktape_EventEmitter_HPTR_LIST); // [dispatcher][hptrList]
count = (int)duk_get_length(ctx, -1);
for (i = 0; i < count; ++i)
{
duk_get_prop_index(ctx, -1, i); // [dispatcher][hptrList][hptr]
hptr = (void**)duk_get_pointer(ctx, -1);
*hptr = dispatcher;
duk_pop(ctx); // [dispatcher][hptrList]
}
return 0;
}
ILibDuktape_EventEmitter* ILibDuktape_EventEmitter_GetEmitter_fromThis(duk_context *ctx)
{
ILibDuktape_EventEmitter *retVal = NULL;
duk_push_this(ctx); // [this]
retVal = ILibDuktape_EventEmitter_GetEmitter_fromCurrent(ctx);
duk_pop(ctx); // ...
return retVal;
}
ILibDuktape_EventEmitter* ILibDuktape_EventEmitter_GetEmitter(duk_context *ctx, duk_idx_t i)
{
ILibDuktape_EventEmitter *retVal = NULL;
if (duk_has_prop_string(ctx, i, ILibDuktape_EventEmitter_TempObject))
{
// This object already has an EventEmitter
duk_get_prop_string(ctx, i, ILibDuktape_EventEmitter_TempObject); // [tmp]
duk_get_prop_string(ctx, -1, ILibDuktape_EventEmitter_Data); // [tmp][data]
retVal = (ILibDuktape_EventEmitter*)Duktape_GetBuffer(ctx, -1, NULL);
duk_pop_2(ctx);
}
return retVal;
}
duk_ret_t ILibDuktape_EventEmitter_removeListener(duk_context *ctx)
{
void *callback = duk_require_heapptr(ctx, 1);
duk_size_t eventNameLen;
char *eventName = Duktape_GetBuffer(ctx, 0, &eventNameLen);
ILibDuktape_EventEmitter *emitter = ILibDuktape_EventEmitter_GetEmitter_fromThis(ctx);
void *eventList;
void *node;
if (emitter != NULL)
{
eventList = ILibHashtable_Get(emitter->eventTable, NULL, eventName, (int)eventNameLen);
if (eventList == NULL) { return(ILibDuktape_Error(ctx, "EventEmitter.removeListener(): Event '%s' not found", eventName)); }
node = ILibLinkedList_GetNode_Search(eventList, NULL, callback);
if (node != NULL)
{
ILibLinkedList_Remove(node);
emitter->totalListeners[0]--;
}
}
return(0);
}
duk_ret_t ILibDuktape_EventEmitter_removeAllListeners(duk_context *ctx)
{
duk_size_t eventNameLen;
char *eventName = Duktape_GetBuffer(ctx, 0, &eventNameLen);
ILibDuktape_EventEmitter *emitter = ILibDuktape_EventEmitter_GetEmitter_fromThis(ctx);
void *eventList, *dispatcher;
int count, i;
void **hptr;
if (emitter != NULL)
{
eventList = ILibHashtable_Get(emitter->eventTable, NULL, eventName, (int)eventNameLen);
if (eventList == NULL) { return(ILibDuktape_Error(ctx, "EventEmitter.removeAllListeners(): Event '%s' not found", eventName)); }
dispatcher = ILibHashtable_Get(emitter->eventTable, ILibDuktape_EventEmitter_SetterFunc, eventName, (int)eventNameLen);
if (dispatcher == NULL) { return(ILibDuktape_Error(ctx, "EventEmitter.removeAllListeners(): Internal error with Event '%s'", eventName)); }
// NULL was passed, we'll need to clear all listeners.
// Start by setting the Native Dispatcher to NULL, so it appears there are no subscribers
duk_push_heapptr(ctx, dispatcher); // [dispatcher]
duk_get_prop_string(ctx, -1, ILibDuktape_EventEmitter_HPTR_LIST); // [dispatcher][hptrList]
count = (int)duk_get_length(ctx, -1);
for (i = 0; i < count; ++i)
{
duk_get_prop_index(ctx, -1, i); // [dispatcher][hptrList][hptr]
hptr = (void**)duk_get_pointer(ctx, -1);
*hptr = NULL;
duk_pop(ctx); // [dispatcher][hptrList]
}
ILibLinkedList_Clear(eventList);
emitter->totalListeners[0] = 0;
}
return(0);
}
ILibDuktape_EventEmitter* ILibDuktape_EventEmitter_Create(duk_context *ctx)
{
ILibDuktape_EventEmitter *retVal;
if (duk_has_prop_string(ctx, -1, ILibDuktape_EventEmitter_TempObject))
{
// This object already has an EventEmitter
duk_get_prop_string(ctx, -1, ILibDuktape_EventEmitter_TempObject); // [tmp]
duk_get_prop_string(ctx, -1, ILibDuktape_EventEmitter_Data); // [tmp][data]
retVal = (ILibDuktape_EventEmitter*)Duktape_GetBuffer(ctx, -1, NULL);
duk_pop_2(ctx);
return retVal;
}
duk_push_object(ctx); // [emitterTmp]
duk_push_fixed_buffer(ctx, sizeof(ILibDuktape_EventEmitter)); // [emitterTmp][data]
retVal = (ILibDuktape_EventEmitter*)Duktape_GetBuffer(ctx, -1, NULL);
memset(retVal, 0, sizeof(ILibDuktape_EventEmitter));
retVal->tmpObject = duk_get_heapptr(ctx, -2);
duk_put_prop_string(ctx, -2, ILibDuktape_EventEmitter_Data); // [emitterTmp]
ILibDuktape_CreateFinalizer(ctx, ILibDuktape_EventEmitter_Finalizer);
duk_put_prop_string(ctx, -2, ILibDuktape_EventEmitter_TempObject); // [...parent...]
retVal->ctx = ctx;
retVal->object = duk_get_heapptr(ctx, -1);
retVal->eventTable = ILibHashtable_Create();
ILibDuktape_CreateInstanceMethodWithIntProperty(ctx, "once", 1, "once", ILibDuktape_EventEmitter_on, 2);
ILibDuktape_CreateInstanceMethodWithIntProperty(ctx, "once", 0, "on", ILibDuktape_EventEmitter_on, 2);
ILibDuktape_CreateInstanceMethod(ctx, "removeListener", ILibDuktape_EventEmitter_removeListener, 2);
ILibDuktape_CreateInstanceMethod(ctx, "removeAllListeners", ILibDuktape_EventEmitter_removeAllListeners, DUK_VARARGS);
ILibDuktape_CreateInstanceMethod(ctx, "emit", ILibDuktape_EventEmitter_emit, DUK_VARARGS);
duk_push_heap_stash(ctx);
if (duk_has_prop_string(ctx, -1, ILibDuktape_EventEmitter_GlobalListenerCount))
{
duk_get_prop_string(ctx, -1, ILibDuktape_EventEmitter_GlobalListenerCount);
retVal->totalListeners = (unsigned int *)Duktape_GetBuffer(ctx, -1, NULL);
duk_pop(ctx);
}
else
{
duk_push_fixed_buffer(ctx, sizeof(unsigned int));
retVal->totalListeners = (unsigned int *)Duktape_GetBuffer(ctx, -1, NULL);
duk_put_prop_string(ctx, -2, ILibDuktape_EventEmitter_GlobalListenerCount);
*(retVal->totalListeners) = 0;
}
duk_pop(ctx);
return retVal;
}
void ILibDuktape_EventEmitter_AddHook(ILibDuktape_EventEmitter *emitter, char *eventName, ILibDuktape_EventEmitter_HookHandler handler)
{
if (ILibHashtable_Get(emitter->eventTable, ILibDuktape_EventEmitter_Hook, eventName, (int)strnlen_s(eventName, 255)) == NULL && handler != NULL)
{
ILibHashtable_Put(emitter->eventTable, ILibDuktape_EventEmitter_Hook, eventName, (int)strnlen_s(eventName, 255), handler);
}
}
duk_ret_t ILibDuktape_EventEmitter_SetEvent(duk_context *ctx)
{
char *propName;
duk_size_t propNameLen;
ILibDuktape_EventEmitter *data;
ILibLinkedList eventList = NULL;
void **hptr;
void *dispatcher;
int i, count;
duk_push_current_function(ctx); // [func]
duk_get_prop_string(ctx, -1, "name"); // [func][name]
propName = (char*)duk_get_lstring(ctx, -1, &propNameLen);
duk_get_prop_string(ctx, -2, ILibDuktape_EventEmitter_DispatcherFunc); // [func][name][dispatcher]
dispatcher = duk_get_heapptr(ctx, -1);
duk_push_this(ctx); // [obj]
duk_get_prop_string(ctx, -1, ILibDuktape_EventEmitter_TempObject); // [this][tmp]
duk_get_prop_string(ctx, -1, ILibDuktape_EventEmitter_Data); // [this][tmp][data]
data = (ILibDuktape_EventEmitter*)Duktape_GetBuffer(ctx, -1, NULL);
eventList = ILibHashtable_Get(data->eventTable, NULL, propName, (int)propNameLen);
if (eventList == NULL) { return(ILibDuktape_Error(ctx, "EventEmitter(): Cannot add listener becuase event '%s' is not found", propName)); }
if (duk_is_null_or_undefined(ctx, 0))
{
// NULL was passed, we'll need to clear all listeners.
// Start by setting the Native Dispatcher to NULL, so it appears there are no subscribers
duk_push_heapptr(ctx, dispatcher); // [dispatcher]
duk_get_prop_string(ctx, -1, ILibDuktape_EventEmitter_HPTR_LIST); // [dispatcher][hptrList]
count = (int)duk_get_length(ctx, -1);
for (i = 0; i < count; ++i)
{
duk_get_prop_index(ctx, -1, i); // [dispatcher][hptrList][hptr]
hptr = (void**)duk_get_pointer(ctx, -1);
*hptr = NULL;
duk_pop(ctx); // [dispatcher][hptrList]
}
ILibLinkedList_Clear(eventList);
}
else
{
void *callback = duk_require_heapptr(ctx, 0);
ILibDuktape_EventEmitter_HookHandler hookHandler = ILibHashtable_Get(data->eventTable, ILibDuktape_EventEmitter_Hook, propName, (int)propNameLen);
ILibLinkedList_AddTail(eventList, callback);
duk_push_heapptr(ctx, data->tmpObject);
duk_push_heapptr(ctx, callback);
duk_put_prop_string(ctx, -2, Duktape_GetStashKey(callback)); // Save callback to tmpObject so it won't get GC'ed
duk_push_heapptr(ctx, dispatcher); // [dispatcher]
duk_get_prop_string(ctx, -1, ILibDuktape_EventEmitter_HPTR_LIST); // [dispatcher][hptrList]
count = (int)duk_get_length(ctx, -1);
for (i = 0; i < count; ++i)
{
duk_get_prop_index(ctx, -1, i); // [dispatcher][hptrList][hptr]
hptr = (void**)duk_get_pointer(ctx, -1);
*hptr = dispatcher; // Set this, so from Native, it looks like there is a subscriber.
duk_pop(ctx); // [dispatcher][hptrList]
}
if (hookHandler != NULL)
{
hookHandler(data, propName, callback);
}
}
return 0;
}
duk_ret_t ILibDuktape_EventEmitter_Dispatcher(duk_context *ctx)
{
int nargs = duk_get_top(ctx);
void *self;
int i;
char *name;
duk_push_current_function(ctx); // [func]
duk_get_prop_string(ctx, -1, "name"); // [func][name]
name = (char*)duk_get_string(ctx, -1);
duk_get_prop_string(ctx, -2, "this"); // [func][name][this]
self = duk_get_heapptr(ctx, -1);
duk_get_prop_string(ctx, -1, "emit"); // [func][name][this][emitter]
//-------------------------------------------------------------------------------------------------
duk_push_heapptr(ctx, self); // [emitter][this]
duk_push_string(ctx, name); // [emitter][this][name]
for (i = 0; i < nargs; ++i)
{
duk_dup(ctx, i); // [emitter][this][name][...args...]
}
duk_call_method(ctx, nargs + 1); // Exception will bubble up.
return 0;
}
duk_ret_t ILibDuktape_EventEmitter_NativeDispatch(duk_context *ctx)
{
int nargs = duk_get_top(ctx);
char *name;
ILibDuktape_EventEmitter_Handler handler;
void *args;
int i = 0;
duk_push_current_function(ctx); // [func]
duk_get_prop_string(ctx, -1, "name"); // [func][name]
name = (char*)duk_get_string(ctx, -1);
duk_get_prop_string(ctx, -2, "handler"); // [func][name][handler]
handler = (ILibDuktape_EventEmitter_Handler)duk_get_pointer(ctx, -1);
duk_push_array(ctx); // [func][name][handler][args]
args = duk_get_heapptr(ctx, -1);
for (i = 0; i < nargs; ++i)
{
duk_dup(ctx, i); // [func][name][handler][args][...arg...]
duk_put_prop_index(ctx, -2, i); // [func][name][handler][args]
}
duk_push_this(ctx);
handler(ctx, duk_get_heapptr(ctx, -1), name, args);
return 0;
}
int ILibDuktape_EventEmitter_AddSink(ILibDuktape_EventEmitter *emitter, char *eventName, ILibDuktape_EventEmitter_Handler handler)
{
ILibLinkedList eventList;
void *func;
duk_push_heapptr(emitter->ctx, emitter->tmpObject); // [tmp]
duk_push_c_function(emitter->ctx, ILibDuktape_EventEmitter_NativeDispatch, DUK_VARARGS); // [tmp][dispatch]
duk_push_string(emitter->ctx, eventName); // [tmp][dispatch][name]
duk_put_prop_string(emitter->ctx, -2, "name"); // [tmp][dispatch]
duk_push_pointer(emitter->ctx, handler); // [tmp][dispatch][nativePtr]
duk_put_prop_string(emitter->ctx, -2, "handler"); // [tmp][dispatch]
func = duk_get_heapptr(emitter->ctx, -1);
eventList = ILibHashtable_Get(emitter->eventTable, NULL, eventName, (int)strnlen_s(eventName, ILibDuktape_EventEmitter_MaxEventNameLen));
if (eventList == NULL) { return 1; }
((int*)ILibLinkedList_GetExtendedMemory(ILibLinkedList_AddTail(eventList, func)))[0] = 2;
emitter->totalListeners[0]++;
duk_put_prop_string(emitter->ctx, -2, Duktape_GetStashKey(func)); // [tmp]
duk_pop(emitter->ctx); // ...
return 0;
}
void ILibDuktape_EventEmitter_RemoveAllEx(ILibHashtable sender, void *Key1, char* Key2, int Key2Len, void *Data, void *user)
{
ILibDuktape_EventEmitter *data = (ILibDuktape_EventEmitter*)user;
if (Key1 == ILibDuktape_EventEmitter_SetterFunc)
{
// If this is not NULL, this is the JavaScript Setter Func
memcpy_s(ILibScratchPad, sizeof(ILibScratchPad), Key2, Key2Len);
ILibScratchPad[Key2Len] = 0;
duk_push_heapptr(data->ctx, Data); // [Setter]
duk_del_prop_string(data->ctx, -1, ILibDuktape_EventEmitter_HPTR_LIST);
duk_push_array(data->ctx); // [Setter][list]
duk_put_prop_string(data->ctx, -2, ILibDuktape_EventEmitter_HPTR_LIST); // [Setter]
duk_pop(data->ctx); // ...
}
}
void ILibDuktape_EventEmitter_RemoveAllListeners(ILibDuktape_EventEmitter *emitter, char *eventName)
{
duk_push_heapptr(emitter->ctx, emitter->object); // [this]
duk_get_prop_string(emitter->ctx, -1, "removeAllListeners"); // [this][func]
duk_swap_top(emitter->ctx, -2); // [func][this]
duk_push_string(emitter->ctx, eventName); // [func][this][eventName]
if (duk_pcall_method(emitter->ctx, 1) != 0) { ILibDuktape_Process_UncaughtExceptionEx(emitter->ctx, "EventEmitter.removeAllListeners(): "); }
duk_pop(emitter->ctx); // ...
}
void ILibDuktape_EventEmitter_RemoveAll(ILibDuktape_EventEmitter *emitter)
{
if (emitter->eventTable != NULL) { ILibHashtable_Enumerate(emitter->eventTable, ILibDuktape_EventEmitter_RemoveAllEx, emitter); }
}
void ILibDuktape_EventEmitter_RemoveEventHeapptr(ILibDuktape_EventEmitter *emitter, char *eventName, void **heapptr)
{
int i, count;
void *dispatcher = NULL;
int eventNameLen = (int)strnlen_s(eventName, ILibDuktape_EventEmitter_MaxEventNameLen);
if ((dispatcher = ILibHashtable_Get(emitter->eventTable, ILibDuktape_EventEmitter_SetterFunc, eventName, eventNameLen)) != NULL)
{
// This event already exists... Let's hook up the hptr to the existing dispatcher
duk_push_heapptr(emitter->ctx, dispatcher); // [dispatcher]
if (heapptr != NULL)
{
duk_get_prop_string(emitter->ctx, -1, ILibDuktape_EventEmitter_HPTR_LIST); // [dispatcher][hptrList]
count = (int)duk_get_length(emitter->ctx, -1);
for (i = 0; i < count; ++i)
{
duk_get_prop_index(emitter->ctx, -1, i); // [dispatcher][hptrList][hptr]
if (duk_get_pointer(emitter->ctx, -1) == heapptr)
{
duk_pop(emitter->ctx); // [dispatcher][hptrList]
duk_del_prop_index(emitter->ctx, -1, i);
break;
}
duk_pop(emitter->ctx); // [dispatcher][hptrList]
}
duk_pop(emitter->ctx); // [dispatcher]
}
else
{
duk_del_prop_string(emitter->ctx, -1, ILibDuktape_EventEmitter_HPTR_LIST); // [dispatcher]
duk_push_array(emitter->ctx); // [dispatcher][hptrList]
duk_put_prop_string(emitter->ctx, -2, ILibDuktape_EventEmitter_HPTR_LIST); // [dispatcher]
}
duk_pop(emitter->ctx); // ...
}
}
int ILibDuktape_EventEmitter_AddEventHeapptr(ILibDuktape_EventEmitter *emitter, char *eventName, void **heapptr)
{
ILibLinkedList eventList = NULL;
void *dispatcher = NULL;
int eventNameLen = (int)strnlen_s(eventName, ILibDuktape_EventEmitter_MaxEventNameLen);
if ((dispatcher = ILibHashtable_Get(emitter->eventTable, ILibDuktape_EventEmitter_SetterFunc, eventName, eventNameLen)) != NULL)
{
// This event already exists... Let's hook up the hptr to the existing dispatcher
duk_push_heapptr(emitter->ctx, dispatcher); // [dispatcher]
duk_get_prop_string(emitter->ctx, -1, ILibDuktape_EventEmitter_HPTR_LIST); // [dispatcher][hptrList]
duk_push_pointer(emitter->ctx, heapptr); // [dispatcher][hptrList][hptr]
duk_put_prop_index(emitter->ctx, -2, (duk_uarridx_t)duk_get_length(emitter->ctx, -2)); // [dispatcher][hptrList]
duk_pop_2(emitter->ctx); // ...
// Now lets check if there was already a subscriber
if ((eventList = ILibHashtable_Get(emitter->eventTable, NULL, eventName, eventNameLen)) != NULL && ILibLinkedList_GetCount(eventList) > 0)
{
*heapptr = dispatcher;
}
return 0;
}
return 1;
}
void ILibDuktape_EventEmitter_CreateEventEx(ILibDuktape_EventEmitter *emitter, char *eventName)
{
void **heapptr;
duk_push_heapptr(emitter->ctx, emitter->tmpObject); // [emitter]
duk_push_object(emitter->ctx); // [emitter][tmp]
duk_push_fixed_buffer(emitter->ctx, sizeof(void*)); // [emitter][tmp][buffer]
heapptr = (void**)Duktape_GetBuffer(emitter->ctx, -1, NULL);
memset((void*)heapptr, 0, sizeof(void*));
duk_put_prop_string(emitter->ctx, -2, "\xFF_buffer"); // [emitter][tmp]
duk_put_prop_string(emitter->ctx, -2, Duktape_GetStashKey(duk_get_heapptr(emitter->ctx, -1))); // [emitter]
duk_pop(emitter->ctx); // ...
ILibDuktape_EventEmitter_CreateEvent(emitter, eventName, heapptr);
}
void ILibDuktape_EventEmitter_CreateEvent(ILibDuktape_EventEmitter *emitter, char *eventName, void **hptr)
{
void *dispatcher = NULL;
int eventNameLen = (int)strnlen_s(eventName, ILibDuktape_EventEmitter_MaxEventNameLen);
if ((dispatcher = ILibHashtable_Get(emitter->eventTable, ILibDuktape_EventEmitter_SetterFunc, eventName, eventNameLen)) != NULL)
{
// This event already exists... Let's hook up the hptr to the existing dispatcher
duk_push_heapptr(emitter->ctx, dispatcher); // [dispatcher]
duk_get_prop_string(emitter->ctx, -1, ILibDuktape_EventEmitter_HPTR_LIST); // [dispatcher][hptrList]
duk_push_pointer(emitter->ctx, hptr); // [dispatcher][hptrList][hptr]
duk_put_prop_index(emitter->ctx, -2, (duk_uarridx_t)duk_get_length(emitter->ctx, -2)); // [dispatcher][hptrList]
duk_pop_2(emitter->ctx); // ...
return;
}
duk_push_heapptr(emitter->ctx, emitter->object); // [obj]
// Create the Property Setter
duk_push_string(emitter->ctx, eventName); // [obj][prop]
duk_push_c_function(emitter->ctx, ILibDuktape_EventEmitter_SetEvent, 1); // [obj][prop][setFunc]
duk_push_string(emitter->ctx, eventName); // [obj][prop][setFunc][name]
duk_put_prop_string(emitter->ctx, -2, "name"); // [obj][prop][setFunc]
// Set some custom properties into the setter func, so we can access it later
duk_push_c_function(emitter->ctx, ILibDuktape_EventEmitter_Dispatcher, DUK_VARARGS); // [obj][prop][setFunc][dispatcher]
dispatcher = duk_get_heapptr(emitter->ctx, -1);
duk_push_heapptr(emitter->ctx, emitter->object); // [obj][prop][setFunc][dispatcher][this]
duk_put_prop_string(emitter->ctx, -2, "this"); // [obj][prop][setFunc][dispatcher]
duk_push_string(emitter->ctx, eventName); // [obj][prop][setFunc][dispatcher][name]
duk_put_prop_string(emitter->ctx, -2, "name"); // [obj][prop][setFunc][dispatcher]
duk_push_array(emitter->ctx); // [obj][prop][setFunc][dispatcher][hptrList]
duk_push_pointer(emitter->ctx, hptr); // [obj][prop][setFunc][dispatcher][hptrList][hptr]
duk_put_prop_index(emitter->ctx, -2, 0); // [obj][prop][setFunc][dispatcher][hptrList]
duk_put_prop_string(emitter->ctx, -2, ILibDuktape_EventEmitter_HPTR_LIST); // [obj][prop][setFunc][dispatcher]
duk_put_prop_string(emitter->ctx, -2, ILibDuktape_EventEmitter_DispatcherFunc); // [obj][prop][setFunc]
duk_def_prop(emitter->ctx, -3, DUK_DEFPROP_FORCE | DUK_DEFPROP_HAVE_SETTER); // [obj]
duk_pop(emitter->ctx); // ...
ILibHashtable_Put(emitter->eventTable, NULL, eventName, eventNameLen, ILibLinkedList_CreateEx(sizeof(int)));
ILibHashtable_Put(emitter->eventTable, ILibDuktape_EventEmitter_SetterFunc, eventName, eventNameLen, dispatcher);
}
void *ILibDuktape_EventEmitter_GetDispatcher(ILibDuktape_EventEmitter *emitter, char *eventName)
{
return ILibHashtable_Get(emitter->eventTable, ILibDuktape_EventEmitter_SetterFunc, eventName, (int)strnlen_s(eventName, ILibDuktape_EventEmitter_MaxEventNameLen));
}
duk_ret_t ILibDuktape_EventEmitter_Inherits_createEvent(duk_context *ctx)
{
char *name = (char*)duk_require_string(ctx, 0);
ILibDuktape_EventEmitter *emitter;
void **hptr;
duk_push_this(ctx); // [emitterUtils]
duk_get_prop_string(ctx, -1, "emitter"); // [emitterUtils][ptr]
emitter = (ILibDuktape_EventEmitter*)duk_get_pointer(ctx, -1);
duk_pop(ctx); // [emitterUtils]
duk_push_fixed_buffer(ctx, sizeof(void*)); // [emitterUtils][buffer]
hptr = (void**)Duktape_GetBuffer(ctx, -1, NULL);
duk_put_prop_string(ctx, -2, name); // [emitterUtils]
ILibDuktape_EventEmitter_CreateEvent(emitter, name, hptr);
return 0;
}
duk_ret_t ILibDuktape_EventEmitter_Inherits_addMethod(duk_context *ctx)
{
ILibDuktape_EventEmitter *emitter;
duk_push_this(ctx); // [emitterUtils]
duk_get_prop_string(ctx, -1, "emitter"); // [emitterUtils][ptr]
emitter = (ILibDuktape_EventEmitter*)duk_get_pointer(ctx, -1);
duk_push_heapptr(ctx, emitter->object); // [emitterUtils][ptr][target]
ILibDuktape_CreateProperty_InstanceMethodEx(ctx, (char*)duk_require_string(ctx, 0), duk_require_heapptr(ctx, 1));
return(0);
}
duk_ret_t ILibDuktape_EventEmitter_Inherits(duk_context *ctx)
{
ILibDuktape_EventEmitter *emitter;
duk_dup(ctx, 0); // [target]
emitter = ILibDuktape_EventEmitter_Create(ctx);
duk_push_object(ctx); // [target][emitterUtils]
duk_dup(ctx, -1); // [target][emitterUtils][dup]
duk_put_prop_string(ctx, -3, "\xFF_emitterUtils"); // [target][emitterUtils]
duk_push_pointer(ctx, emitter); // [target][emitterUtils][ptr]
duk_put_prop_string(ctx, -2, "emitter"); // [target][emitterUtils]
ILibDuktape_CreateInstanceMethod(ctx, "createEvent", ILibDuktape_EventEmitter_Inherits_createEvent, 1);
ILibDuktape_CreateInstanceMethod(ctx, "addMethod", ILibDuktape_EventEmitter_Inherits_addMethod, 2);
return 1;
}
void ILibDuktape_EventEmitter_PUSH(duk_context *ctx, void *chain)
{
duk_push_object(ctx); // [emitter]
ILibDuktape_CreateInstanceMethod(ctx, "inherits", ILibDuktape_EventEmitter_Inherits, 1);
}
void ILibDuktape_EventEmitter_Init(duk_context *ctx)
{
ILibDuktape_ModSearch_AddHandler(ctx, "events", ILibDuktape_EventEmitter_PUSH);
}