1
0
mirror of https://github.com/Ylianst/MeshAgent synced 2025-12-06 00:13:33 +00:00
Files
MeshAgent/microscript/ILibduktape_EventEmitter.c
Ylian Saint-Hilaire 7adf0dddd4 Updated headers.
2022-02-04 14:43:55 -08:00

1714 lines
75 KiB
C

/*
Copyright 2006 - 2022 Intel Corporation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#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"
#include "ILibDuktape_Polyfills.h"
#define ILibDuktape_EventEmitter_MaxEventNameLen 255
#define ILibDuktape_EventEmitter_EmitterUtils "\xFF_emitterUtils"
#define ILibDuktape_EventEmitter_Data "\xFF_EventEmitter_Data"
#define ILibDuktape_EventEmitter_RetVal "\xFF_EventEmitter_RetVal"
#define ILibDuktape_EventEmitter_LastRetValueTable "\xFF_EventEmitter_LastRetValueTable"
#define ILibDuktape_EventEmitter_GlobalListenerCount "\xFF_EventEmitter_GlobalListenerCount"
#define ILibDuktape_EventEmitter_Forward_SourceName "\xFF_EventEmitter_SourceName"
#define ILibDuktape_EventEmitter_Forward_TargetName "\xFF_EventEmitter_TargetName"
#define ILibDuktape_EventEmitter_Forward_SourceObject "\xFF_EventEmitter_SourceObject"
#define ILibDuktape_EventEmitter_ForwardTable "\xFF_EventEmitter_ForwardTable"
#define ILibDuktape_EventEmitter_EventTable "\xFF_EventEmitter_EventTable"
#define ILibDuktape_EventEmitter_CountTable "\xFF_EventEmitter_CountTable"
#define ILibDuktape_EventEmitter_References "\xFF_EventReferences"
extern void ILibDuktape_GenericMarshal_Variable_PUSH(duk_context *ctx, void *ptr, int size);
typedef struct ILibDuktape_EventEmitter_EmitStruct
{
void *func;
int once;
}ILibDuktape_EventEmitter_EmitStruct;
#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);
}
int ILibDuktape_EventEmitter_HasListeners2(ILibDuktape_EventEmitter *emitter, char *eventName, int defaultValue)
{
char numtmp[32];
int retVal = defaultValue;
if (emitter != NULL && ILibMemory_CanaryOK(emitter) && duk_ctx_is_alive(emitter->ctx))
{
ILibSpinLock_Lock(&(emitter->listenerCountTableLock));
if (emitter->listenerCountTableLength > 2 && emitter->listenerCountTableLength < INT32_MAX)
{
size_t eventNameLen = strnlen_s(eventName, ILibDuktape_EventEmitter_MaxEventNameLen);
parser_result *pr = ILibParseString((char*)emitter->listenerCountTable, 1, (int)emitter->listenerCountTableLength - 2, ",", 1);
if (pr->NumResults > 0)
{
parser_result *pr2 = NULL;
parser_result_field *f = pr->FirstResult;
while (f != NULL)
{
if (f->datalength > 2)
{
pr2 = ILibParseString(f->data + 1, 0, f->datalength - 2, "=", 1);
if (pr2->NumResults == 2)
{
if (eventNameLen == pr2->FirstResult->datalength && strncmp(eventName, pr2->FirstResult->data, eventNameLen) == 0)
{
if (sizeof(numtmp) > pr2->LastResult->datalength)
{
memcpy_s(numtmp, sizeof(numtmp), pr2->LastResult->data, pr2->LastResult->datalength);
numtmp[pr2->LastResult->datalength] = 0;
retVal = ILib_atoi2_int32(numtmp, sizeof(numtmp));
ILibDestructParserResults(pr2);
break;
}
}
}
ILibDestructParserResults(pr2);
}
f = f->NextResult;
}
}
ILibDestructParserResults(pr);
}
ILibSpinLock_UnLock(&(emitter->listenerCountTableLock));
}
return(retVal);
}
duk_ret_t ILibDuktape_EventEmitter_DefaultNewListenerHandler(duk_context *ctx)
{
char *currentEventName = (char*)duk_require_string(ctx, 0);
duk_push_current_function(ctx);
char *name = Duktape_GetStringPropertyValue(ctx, -1, "event_name", NULL);
void *callback = Duktape_GetPointerProperty(ctx, -1, "event_callback");
if (strcmp(name, currentEventName) == 0)
{
duk_push_heapptr(ctx, callback); // [callback]
duk_push_this(ctx); // [callback][this]
duk_dup(ctx, 0); // [callback][this][name]
duk_dup(ctx, 1); // [callback][this][name][handler]
if (duk_pcall_method(ctx, 2) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ctx, "EventEmitter.DefaultNewListenerHandler() "); }
duk_pop(ctx); // ...
}
return(0);
}
void ILibDuktape_EventEmitter_emit_removeListener(duk_context *ctx, const char* eventName, duk_idx_t objix, duk_idx_t listenerix)
{
ILibDuktape_EventEmitter_SetupEmitEx(ctx, objix, "removeListener"); // [emit][this][removeListener]
duk_push_string(ctx, eventName); // [emit][this][removeListener][eventName]
duk_get_prop_string(ctx, listenerix < 0 ? listenerix - 4 : listenerix, "func"); // [emit][this][removeListener][eventName][func]
if (duk_pcall_method(ctx, 3) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ctx, "events.onRemoveListener(%s) error ", eventName); }
duk_pop(ctx); // ...
}
duk_ret_t ILibDuktape_EventEmitter_emit(duk_context *ctx)
{
int nargs = duk_get_top(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);
duk_size_t arrSize, arrIndex;
int j;
int emitted = 0;
duk_require_stack(ctx, 4 + nargs + (2*DUK_API_ENTRY_STACK)); // This will make sure we have enough stack space to get the emitter object
duk_push_this(ctx); // [object]
ILibDuktape_EventEmitter *data = (ILibDuktape_EventEmitter*)Duktape_GetBufferProperty(ctx, -1, ILibDuktape_EventEmitter_Data);
if (!ILibMemory_CanaryOK(data)) { return(0); } // This object has been finalized already, so we need to abort
char *objid = Duktape_GetStringPropertyValue(ctx, -1, ILibDuktape_OBJID, "unknown");
duk_push_heapptr(ctx, data->retValTable); // [object][retTable]
duk_push_heapptr(ctx, data->table); // [object][retTable][table]
if (!duk_has_prop_string(ctx, -1, name))
{
if (data->eventType == ILibDuktape_EventEmitter_Type_IMPLICIT)
{
duk_push_false(ctx); return(1);
}
else
{
return ILibDuktape_Error(ctx, "EventEmitter.emit(): Event '%s' not found on object '%s'", name, objid);
}
}
duk_get_prop_string(ctx, -1, name); // [object][retTable][table][array]
duk_array_clone(ctx, -1); // [object][retTable][table][array][array]
do
{
arrSize = duk_get_length(ctx, -2);
for (arrIndex = 0; arrIndex < arrSize; ++arrIndex)
{
if (Duktape_GetIntPropertyValueFromHeapptr(ctx, Duktape_GetHeapptrIndexProperty(ctx, -2, (duk_uarridx_t)arrIndex), "once", 0) != 0)
{
// This handler is a 'once' handler, so we need to delete it here
duk_get_prop_index(ctx, -2, (duk_uarridx_t)arrIndex); // [object][retTable][table][array][array][listener]
duk_array_remove(ctx, -3, (duk_uarridx_t)arrIndex);
// Now we need to emit 'removeListener'
ILibDuktape_EventEmitter_emit_removeListener(ctx, name, -6, -1);// [object][retTable][table][array][array][listener]
duk_pop(ctx); // [object][retTable][table][array][array]
break;
}
}
} while (arrIndex < arrSize);
duk_remove(ctx, -2); // [object][retTable][table][array]
// Before we dispatch, lets clear our last return values for this event
duk_push_heapptr(ctx, data->retValTable); // [object][retTable][table][array][retTable]
duk_del_prop_lstring(ctx, -1, name, nameLen);
duk_pop(ctx); // [object][retTable][table][array]
duk_del_prop_string(ctx, -4, ILibDuktape_EventEmitter_RetVal);
data->lastReturnValue = NULL;
while (duk_get_length(ctx, -1) > 0)
{
emitted = 1;
duk_array_shift(ctx, -1); // [object][retTable][table][array][J]
duk_get_prop_string(ctx, -1, "func"); // [object][retTable][table][array][J][func]
duk_push_this(ctx); // [object][retTable][table][array][J][func][this]
for (j = 1; j < nargs; ++j)
{
duk_dup(ctx, j); // [object][retTable][table][array][J][func][this][..args..]
}
ILibDuktape_ExecutorTimeout_Start(ctx);
if (duk_pcall_method(ctx, nargs - 1) != 0) // [object][retTable][table][array][J][ret]
{
ILibDuktape_ExecutorTimeout_Stop(ctx);
// Invocation Error
if (strcmp(duk_safe_to_string(ctx, -1), "Process.exit() forced script termination") == 0)
{
duk_dup(ctx, -1);
duk_throw(ctx);
return(DUK_RET_ERROR);
}
else
{
duk_get_prop_string(ctx, -2, "func"); // [object][retTable][table][array][J][e][func]
return(ILibDuktape_Error(ctx, "EventEmitter.emit(): Event dispatch for '%s' on '%s' threw an exception: %s in method '%s()'", name, objid, duk_safe_to_string(ctx, -2), Duktape_GetStringPropertyValue(ctx, -1, "name", "unknown_method")));
}
}
ILibDuktape_ExecutorTimeout_Stop(ctx);
if (!duk_is_undefined(ctx, -1)) // [object][retTable][table][array][J][ret]
{
duk_dup(ctx, -1); // [object][retTable][table][array][J][ret][ret]
duk_put_prop_string(ctx, -7, ILibDuktape_EventEmitter_RetVal); // [object][retTable][table][array][J][ret]
data->lastReturnValue = duk_get_heapptr(ctx, -1);
duk_put_prop_lstring(ctx, -5, name, nameLen); // [object][retTable][table][array][J]
duk_pop(ctx); // [object][retTable][table][array]
}
else
{
duk_pop_2(ctx); // [object][retTable][table][array]
}
}
duk_push_boolean(ctx, emitted);
return(1);
}
int ILibDuktape_EventEmitter_PrependOnce(duk_context *ctx, duk_idx_t i, char *eventName, duk_c_function func)
{
int retVal = 1;
duk_dup(ctx, i); // [this]
duk_get_prop_string(ctx, -1, "prependOnceListener"); // [this][prependOnce]
duk_swap_top(ctx, -2); // [prependOnce][this]
duk_push_string(ctx, eventName); // [prependOnce][this][eventName]
duk_push_c_function(ctx, func, DUK_VARARGS); // [prependOnce][this][eventName][func]
if (duk_pcall_method(ctx, 2) != 0) { retVal = 0; }
duk_pop(ctx); // ...
return(retVal);
}
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_AddOnceEx3(duk_context *ctx, duk_idx_t idx, char *eventName, duk_c_function func)
{
int retVal = 1;
duk_prepare_method_call(ctx, idx, "once"); // [once][this]
duk_push_string(ctx, eventName); // [once][this][event]
duk_push_c_function(ctx, func, DUK_VARARGS); // [once][this][event][func]
retVal = duk_pcall_method(ctx, 2); // [ret]
duk_pop(ctx); // ...
return(retVal);
}
int ILibDuktape_EventEmitter_AddOnceEx(ILibDuktape_EventEmitter *emitter, char *eventName, duk_c_function func, duk_idx_t funcArgs)
{
int retVal = 1;
duk_push_heapptr(emitter->ctx, emitter->object); // [obj]
duk_prepare_method_call(emitter->ctx, -1, "once"); // [obj][once][this]
duk_push_string(emitter->ctx, eventName); // [obj][once][this][eventName]
duk_push_c_function(emitter->ctx, func, funcArgs); // [obj][once][this][eventName][func]
retVal = duk_pcall_method(emitter->ctx, 2); // [obj][ret]
duk_pop_2(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;
int prepend;
duk_require_stack(ctx, 10);
duk_push_current_function(ctx);
once = Duktape_GetIntPropertyValue(ctx, -1, "once", 0);
prepend = Duktape_GetIntPropertyValue(ctx, -1, "prepend", 0);
duk_push_this(ctx); // [object]
data = (ILibDuktape_EventEmitter*)Duktape_GetBufferProperty(ctx, -1, ILibDuktape_EventEmitter_Data);
duk_push_heapptr(ctx, data->table); // [object][table]
if (!duk_has_prop_string(ctx, -1, propName))
{
if (data->eventType == ILibDuktape_EventEmitter_Type_EXPLICIT) { return(ILibDuktape_Error(ctx, "Cannot register for non-existing event: %s", propName)); }
duk_push_array(ctx);
duk_put_prop_string(ctx, -2, propName);
}
duk_get_prop_string(ctx, -1, propName); // [object][table][array]
duk_push_object(ctx); // [object][table][array][handler]
duk_dup(ctx, 1); duk_put_prop_string(ctx, -2, "func");
duk_push_int(ctx, once); duk_put_prop_string(ctx, -2, "once");
if (!(propNameLen == 11 && strncmp(propName, "newListener", 11) == 0) && !(propNameLen == 12 && strncmp(propName, "newListener2", 12) == 0))
{
// Only emit 'newListener' when the event itself isn't 'newListener' or 'newListener2'
ILibDuktape_EventEmitter_SetupEmit(ctx, data->object, "newListener"); // [emit][this][newListener]
duk_push_lstring(ctx, propName, propNameLen); // [emit][this][newListener][propName]
duk_push_heapptr(ctx, callback); // [emit][this][newListener][propName][callback]
duk_call_method(ctx, 3); duk_pop(ctx); // ...
}
if (prepend != 0)
{
duk_array_unshift(ctx, -2); // [object][table][array]
}
else
{
duk_array_push(ctx, -2); // [object][table][array]
}
if (!(propNameLen == 11 && strncmp(propName, "newListener", 11) == 0) && !(propNameLen == 12 && strncmp(propName, "newListener2", 12) == 0))
{
// Only emit 'newListener2' when the event itself isn't 'newListener' or 'newListener2'
ILibDuktape_EventEmitter_SetupEmit(ctx, data->object, "newListener2"); // [emit][this][newListener2]
duk_push_lstring(ctx, propName, propNameLen); // [emit][this][newListener2][propName]
duk_push_heapptr(ctx, callback); // [emit][this][newListener2][propName][callback]
duk_call_method(ctx, 3); duk_pop(ctx); // ...
}
duk_push_this(ctx);
return 1;
}
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)
{
if (!duk_ctx_is_alive(ctx)) { return(NULL); }
ILibDuktape_EventEmitter *retVal = (ILibDuktape_EventEmitter*)Duktape_GetBufferProperty(ctx, i, ILibDuktape_EventEmitter_Data);
if (!duk_ctx_is_alive(retVal->object)) { retVal = NULL; }
return retVal;
}
duk_ret_t ILibDuktape_EventEmitter_removeListener(duk_context *ctx)
{
char *eventName = (char*)duk_require_string(ctx, 0);
void *func = duk_require_heapptr(ctx, 1);
duk_size_t arrSize;
duk_uarridx_t arrIndex;
duk_push_this(ctx); // [object]
duk_get_prop_string(ctx, -1, ILibDuktape_EventEmitter_EventTable); // [object][table]
if (duk_has_prop_string(ctx, -1, eventName))
{
duk_get_prop_string(ctx, -1, eventName); // [object][table][array]
arrSize = duk_get_length(ctx, -1);
for (arrIndex = 0; arrIndex < arrSize; ++arrIndex)
{
if (Duktape_GetHeapptrPropertyValueFromHeapptr(ctx, Duktape_GetHeapptrIndexProperty(ctx, -1, arrIndex), "func") == func)
{
duk_get_prop_index(ctx, -1, arrIndex); // [object][table][array][listener]
duk_array_remove(ctx, -2, arrIndex); // [object][table][array][listener]
ILibDuktape_EventEmitter_emit_removeListener(ctx, eventName, -4, -1);
duk_pop(ctx); // [object][table][array]
break;
}
}
}
return(0);
}
duk_ret_t ILibDuktape_EventEmitter_removeAllListeners_AllEvents_NonInfrastructure_find(duk_context *ctx)
{
duk_dup(ctx, 0); // [element]
duk_get_prop_string(ctx, -1, "func"); // [element][func]
if (Duktape_GetBooleanProperty(ctx, -1, ILibDuktape_EventEmitter_InfrastructureEvent, 0) == 0)
{
duk_push_true(ctx);
}
else
{
duk_push_false(ctx);
}
return(1);
}
duk_ret_t ILibDuktape_EventEmitter_removeAllListeners_AllEvents_NonInfrastructure(duk_context *ctx)
{
int i = -1;
duk_push_this(ctx); // [emitter]
duk_get_prop_string(ctx, -1, ILibDuktape_EventEmitter_EventTable); // [emitter][table]
duk_enum(ctx, -1, DUK_ENUM_OWN_PROPERTIES_ONLY); // [emitter][table][enum]
while (duk_next(ctx, -1, 1)) // [emitter][table][enum][name][array]
{
// Skip finalizers, as well as removeListener
if (strcmp(duk_get_string(ctx, -2), "~") == 0 || strcmp(duk_get_string(ctx, -2), "removeListener") == 0) { duk_pop_2(ctx); continue; }
do
{
duk_prepare_method_call(ctx, -1, "findIndex"); // [emitter][table][enum][name][array][findIndex][this]
duk_push_c_function(ctx, ILibDuktape_EventEmitter_removeAllListeners_AllEvents_NonInfrastructure_find, DUK_VARARGS);//][func]
duk_call_method(ctx, 1); // [emitter][table][enum][name][array][index]
if ((i = duk_get_int(ctx, -1)) != -1)
{
duk_get_prop_index(ctx, -2, i); // [emitter][table][enum][name][array][index][element]
duk_prepare_method_call(ctx, -3, "splice"); // [emitter][table][enum][name][array][index][element][splice][this]
duk_push_int(ctx, i); // [emitter][table][enum][name][array][index][element][splice][this][start]
duk_push_int(ctx, 1); // [emitter][table][enum][name][array][index][element][splice][this][start][1]
duk_call_method(ctx, 2); duk_pop(ctx); // [emitter][table][enum][name][array][index][element]
ILibDuktape_EventEmitter_emit_removeListener(ctx, duk_get_string(ctx, -4), -7, -1);
duk_pop(ctx); // [emitter][table][enum][name][array][index]
}
duk_pop(ctx); // [emitter][table][enum][name][array]
} while (i != -1);
duk_pop_2(ctx); // [emitter][table][enum]
}
duk_pop(ctx); // [emitter][table]
if (duk_has_prop_string(ctx, -1, "removeListener"))
{
i = -1;
duk_get_prop_string(ctx, -1, "removeListener"); // [emitter][table][array]
do
{
duk_prepare_method_call(ctx, -1, "findIndex"); // [emitter][table][array][findIndex][this]
duk_push_c_function(ctx, ILibDuktape_EventEmitter_removeAllListeners_AllEvents_NonInfrastructure_find, DUK_VARARGS);//][func]
duk_call_method(ctx, 1); // [emitter][table][array][index]
if ((i = duk_get_int(ctx, -1)) != -1)
{
duk_get_prop_index(ctx, -2, i); // [emitter][table][array][index][element]
duk_prepare_method_call(ctx, -3, "splice"); // [emitter][table][array][index][element][splice][this]
duk_push_int(ctx, i); // [emitter][table][array][index][element][splice][this][start]
duk_push_int(ctx, 1); // [emitter][table][array][index][element][splice][this][start][1]
duk_call_method(ctx, 2); duk_pop(ctx); // [emitter][table][array][index][element]
ILibDuktape_EventEmitter_emit_removeListener(ctx, "removeListener", -5, -1);
duk_pop(ctx); // [emitter][table][array][index]
}
duk_pop(ctx); // [emitter][table][array]
} while (i != -1);
}
return(0);
}
duk_ret_t ILibDuktape_EventEmitter_removeAllListeners(duk_context *ctx)
{
if (duk_get_top(ctx) == 0) { return(ILibDuktape_EventEmitter_removeAllListeners_AllEvents_NonInfrastructure(ctx)); }
char *eventName = (char*)duk_require_string(ctx, 0);
duk_push_this(ctx); // [object]
duk_get_prop_string(ctx, -1, ILibDuktape_EventEmitter_EventTable); // [object][table]
if (duk_has_prop_string(ctx, -1, eventName))
{
duk_get_prop_string(ctx, -1, eventName); // [object][table][array]
duk_array_clone(ctx, -1); // [object][table][array][clone]
duk_del_prop_string(ctx, -3, eventName); // [object][table][array][clone]
duk_remove(ctx, -2); // [object][table][clone]
duk_push_array(ctx); // [object][table][clone][empty]
duk_put_prop_string(ctx, -3, eventName); // [object][table][clone]
while (duk_get_length(ctx, -1) > 0)
{
duk_array_shift(ctx, -1); // [object][table][clone][listener]
ILibDuktape_EventEmitter_emit_removeListener(ctx, eventName, -4, -1);
duk_pop(ctx); // [object][table][clone]
}
}
return(0);
}
duk_ret_t ILibDuktape_EventEmitter_EmbeddedFinalizer(duk_context *ctx)
{
ILibDuktape_EventEmitter *data = ILibDuktape_EventEmitter_GetEmitter(ctx, 0);
if (!ILibMemory_CanaryOK(data) || !duk_ctx_is_alive(data->ctx)) { return(0); }
ILibDuktape_EventEmitter_SetupEmit(ctx, duk_get_heapptr(ctx, 0), "~"); // [emit][this][~]
duk_dup(ctx, 0); // [emit][this][~][self]
char *meta = Duktape_GetStringPropertyValue(ctx, -1, ILibDuktape_OBJID, "UNKNOWN");
char *debugmsg;
if (g_displayFinalizerMessages)
{
debugmsg = Duktape_GetStringPropertyValue(ctx, -1, ILibDuktape_EventEmitter_FinalizerDebugMessage, NULL);
printf("+-+- Finalizer Event for: %s [%p] -+-+\n", Duktape_GetStringPropertyValue(ctx, -1, ILibDuktape_OBJID, "UNKNOWN"), duk_get_heapptr(ctx, -1));
if (debugmsg != NULL) { printf(" => %s\n", debugmsg); }
if (strcmp(Duktape_GetStringPropertyValue(ctx, -1, ILibDuktape_OBJID, "UNKNOWN"), "UNKNOWN") == 0)
{
int first = 1;
ILibDuktape_EventEmitter *emitter = ILibDuktape_EventEmitter_GetEmitter(ctx, -1);
if (emitter != NULL)
{
printf("UNKNOWN: Listeners=%d\n", ILibDuktape_EventEmitter_HasListeners(emitter, "~"));
duk_enum(ctx, -1, DUK_ENUM_OWN_PROPERTIES_ONLY); // [enumerator]
while (duk_next(ctx, -1, 1))
{
printf("Key: %s, Val: %s\n", duk_get_string(ctx, -2), duk_get_string(ctx, -1));// [enumerator][key][val]
duk_pop_2(ctx); // [enumerator]
}
duk_pop(ctx); // ...
printf("Event Names: ");
duk_push_heapptr(ctx, emitter->table); // [table]
duk_enum(ctx, -1, DUK_ENUM_OWN_PROPERTIES_ONLY); // [table][enumerator]
while (duk_next(ctx, -1, 0)) // [table][enumerator][key]
{
printf("%s%s", (first == 0 ? ", " : ""), duk_get_string(ctx, -1));
first = 0;
duk_pop(ctx); // [table][enumerator]
}
duk_pop_2(ctx); // ...
printf("\n");
}
}
}
if (duk_pcall_method(ctx, 2) != 0)
{
ILibDuktape_Process_UncaughtExceptionEx(ctx, "Error in Finalizer (%s): [Invalid C function means you forgot to return 0] ", meta);
}
return(0);
}
duk_ret_t ILibDuktape_EventEmitter_emitReturnValue(duk_context *ctx)
{
int retVal = 1;
int nargs = duk_get_top(ctx);
duk_push_this(ctx); // [this]
switch (nargs)
{
case 0:
duk_get_prop_string(ctx, -1, ILibDuktape_EventEmitter_RetVal); // [this][retVal]
break;
case 1:
duk_get_prop_string(ctx, -1, ILibDuktape_EventEmitter_LastRetValueTable); // [this][table]
duk_dup(ctx, 0); // [this][table][key]
duk_get_prop(ctx, -2); // [this][table][val]
break;
case 2:
duk_get_prop_string(ctx, -1, ILibDuktape_EventEmitter_LastRetValueTable); // [this][table]
duk_dup(ctx, 0); // [this][table][key]
duk_dup(ctx, 1); // [this][table][key][value]
duk_put_prop(ctx, -3);
retVal = 0;
break;
default:
retVal = ILibDuktape_Error(ctx, "INVALID Parameter Count");
break;
}
return(retVal);
}
duk_ret_t ILibDuktape_EventEmitter_listenerCount(duk_context *ctx)
{
char *name = (char*)duk_require_string(ctx, 0);
duk_push_this(ctx); // [events]
duk_get_prop_string(ctx, -1, ILibDuktape_EventEmitter_EventTable); // [events][table]
if (duk_has_prop_string(ctx, -1, name))
{
duk_get_prop_string(ctx, -1, name); // [events][table][array]
duk_push_uint(ctx, (duk_uint_t)duk_get_length(ctx, -1)); // [events][table][array][len]
}
else
{
duk_push_uint(ctx, 0);
}
return(1);
}
duk_ret_t ILibDuktape_EventEmitter_eventNames(duk_context *ctx)
{
duk_size_t len;
duk_uarridx_t i;
duk_size_t count;
ILibDuktape_EventEmitter *emitter = ILibDuktape_EventEmitter_GetEmitter_fromThis(ctx);
duk_push_array(ctx); // [array]
duk_push_heapptr(ctx, emitter->table); // [array][table]
duk_enum(ctx, -1, DUK_ENUM_OWN_PROPERTIES_ONLY);// [array][table][enumerator]
while (duk_next(ctx, -1, 1)) // [array][table][enumerator][key][value]
{
count = 0;
if (strcmp(duk_get_string(ctx, -2), "~") != 0)
{
// We will only look at events that aren't the finalizer event
len = duk_get_length(ctx, -1);
for (i = 0; i < len; ++i)
{
duk_get_prop_index(ctx, -1, i); // [array][table][enumerator][key][value][obj]
duk_get_prop_string(ctx, -1, "func"); // [array][table][enumerator][key][value][obj][func]
if (Duktape_GetBooleanProperty(ctx, -1, ILibDuktape_EventEmitter_InfrastructureEvent, 0) == 0) { ++count; }
duk_pop_2(ctx); // [array][table][enumerator][key][value]
}
}
duk_pop(ctx); // [array][table][enumerator][key]
if (count > 0)
{
duk_array_push(ctx, -4); // [array][table][enumerator]
}
else
{
duk_pop(ctx); // [array][table][enumerator]
}
}
duk_pop_2(ctx); // [array]
return(1);
}
duk_ret_t ILibDuktape_EventEmitter_listeners(duk_context *ctx)
{
duk_uarridx_t x;
duk_size_t sz;
char *eventName = (char*)duk_require_string(ctx, 0);
duk_push_this(ctx); // [object]
duk_get_prop_string(ctx, -1, ILibDuktape_EventEmitter_EventTable); // [object][table]
duk_push_array(ctx); // [object][table][array]
if (duk_has_prop_string(ctx, -2, eventName))
{
duk_get_prop_string(ctx, -2, eventName); // [object][table][array][handlers]
sz = duk_get_length(ctx, -1);
for (x = 0; x < sz; ++x)
{
duk_push_heapptr(ctx, Duktape_GetHeapptrPropertyValueFromHeapptr(ctx, Duktape_GetHeapptrIndexProperty(ctx, -1, x), "func"));
duk_array_push(ctx, -3);
}
duk_pop(ctx); // [object][table][array]
}
return(1);
}
duk_ret_t ILibDuktape_EventEmitter_listeners_tableinit_findIndex(duk_context *ctx)
{
duk_push_current_function(ctx); // [func]
char *eventName = Duktape_GetStringPropertyValue(ctx, -1, "eventName", "");
duk_dup(ctx, 0); // [func][string]
duk_string_split(ctx, -1, "="); // [func][string][array]
duk_array_shift(ctx, -1); // [func][string][array][string]
if (strcmp(duk_get_string(ctx, -1), eventName) == 0)
{
duk_push_true(ctx);
}
else
{
duk_push_false(ctx);
}
return(1);
}
duk_ret_t ILibDuktape_EventEmitter_listeners_tableinit(duk_context *ctx)
{
duk_push_current_function(ctx);
int isAdd = Duktape_GetIntPropertyValue(ctx, -1, "add", 0);
const char *eventName = duk_require_string(ctx, 0);
duk_push_this(ctx); // [obj]
ILibDuktape_EventEmitter *emitter = (ILibDuktape_EventEmitter*)Duktape_GetBufferProperty(ctx, -1, ILibDuktape_EventEmitter_Data);
ILibSpinLock_Lock(&(emitter->listenerCountTableLock));
duk_push_global_object(ctx); // [g]
duk_get_prop_string(ctx, -1, "JSON"); // [g][JSON]
duk_prepare_method_call(ctx, -1, "parse"); // [g][JSON][parse][this]
duk_push_string(ctx, emitter->listenerCountTable); // [g][JSON][parse][this][string]
if (duk_pcall_method(ctx, 1) == 0) // [g][JSON][array]
{
duk_prepare_method_call(ctx, -1, "findIndex"); // [g][JSON][array][findIndex][this]
duk_push_c_function(ctx, ILibDuktape_EventEmitter_listeners_tableinit_findIndex, DUK_VARARGS);// .[this][func]
duk_push_string(ctx, eventName); duk_put_prop_string(ctx, -2, "eventName");
if (duk_pcall_method(ctx, 1) == 0) // [g][JSON][array][index]
{
int index = duk_get_int(ctx, -1);
if (duk_is_number(ctx, -1) && duk_get_int(ctx, -1) >= 0)
{
duk_get_prop_index(ctx, -2, duk_get_uint(ctx, -1)); // [g][JSON][array][index][string]
}
else
{
duk_push_sprintf(ctx, "%s=0", eventName); // [g][JSON][array][index][string]
}
duk_string_split(ctx, -1, "="); // [g][JSON][array][index][string][array]
duk_array_pop(ctx, -1); // [g][JSON][array][index][string][array][int]
int v = duk_to_int(ctx, -1); // [g][JSON][array][index][string][array][int]
if (isAdd)
{
++v;
}
else
{
if (--v < 0) { v = 0; }
}
duk_push_sprintf(ctx, "%d", v); // [g][JSON][array][index][string][array][int][new]
duk_array_push(ctx, -3); // [g][JSON][array][index][string][array][int]
duk_pop(ctx); // [g][JSON][array][index][string][array]
duk_array_join(ctx, -1, "="); // [g][JSON][array][index][string][array][string]
const char *tmp = duk_get_string(ctx, -1);
if (index < 0)
{
duk_array_push(ctx, -5); // [g][JSON][array][index][string][array]
duk_push_string(ctx, tmp); // [g][JSON][array][index][string][array][string]
}
else
{
duk_array_replace(ctx, -5, index, tmp);
}
duk_prepare_method_call(ctx, -6, "stringify"); // [g][JSON][array][index][string][array][string][stringify][this]
duk_dup(ctx, -7); // [g][JSON][array][index][string][array][string][stringify][this][array]
if (duk_pcall_method(ctx, 1) == 0) // [g][JSON][array][index][string][array][string][string]
{
duk_size_t len = 0;
emitter->listenerCountTable = duk_get_lstring(ctx, -1, &len);
emitter->listenerCountTableLength = len;
duk_push_this(ctx); // [g][JSON][array][index][string][array][string][string][this]
duk_swap_top(ctx, -2); // [g][JSON][array][index][string][array][string][this][string]
ILibDuktape_CreateReadonlyProperty_SetEnumerable(ctx, ILibDuktape_EventEmitter_CountTable, 0);
}
}
}
ILibSpinLock_UnLock(&(emitter->listenerCountTableLock));
return(0);
}
ILibDuktape_EventEmitter* ILibDuktape_EventEmitter_Create(duk_context *ctx)
{
ILibDuktape_EventEmitter *retVal;
if (duk_has_prop_string(ctx, -1, ILibDuktape_EventEmitter_Data))
{
// This object already has an EventEmitter
return((ILibDuktape_EventEmitter*)Duktape_GetBufferProperty(ctx, -1, ILibDuktape_EventEmitter_Data));
}
retVal = (ILibDuktape_EventEmitter*)Duktape_PushBuffer(ctx, sizeof(ILibDuktape_EventEmitter)); // [event][data]
ILibDuktape_CreateReadonlyProperty_SetEnumerable(ctx, ILibDuktape_EventEmitter_Data, 0); // [event]
retVal->ctx = ctx;
retVal->object = duk_get_heapptr(ctx, -1);
duk_push_object(ctx);
retVal->table = duk_get_heapptr(ctx, -1);
ILibDuktape_CreateReadonlyProperty_SetEnumerable(ctx, ILibDuktape_EventEmitter_EventTable, 0);
duk_push_object(ctx);
retVal->retValTable = duk_get_heapptr(ctx, -1);
ILibDuktape_CreateReadonlyProperty_SetEnumerable(ctx, ILibDuktape_EventEmitter_LastRetValueTable, 0);
ILibSpinLock_Init(&(retVal->listenerCountTableLock));
retVal->listenerCountTable = (char*)"[]";
retVal->listenerCountTableLength = 2;
ILibDuktape_CreateInstanceMethodWithProperties(ctx, "once", ILibDuktape_EventEmitter_on, 2, 2, "once", duk_push_int_ex(ctx, 1), "prepend", duk_push_int_ex(ctx, 0));
ILibDuktape_CreateInstanceMethodWithProperties(ctx, "on", ILibDuktape_EventEmitter_on, 2, 2, "once", duk_push_int_ex(ctx, 0), "prepend", duk_push_int_ex(ctx, 0));
ILibDuktape_CreateInstanceMethodWithProperties(ctx, "prependOnceListener", ILibDuktape_EventEmitter_on, 2, 2, "once", duk_push_int_ex(ctx, 1), "prepend", duk_push_int_ex(ctx, 1));
ILibDuktape_CreateInstanceMethodWithProperties(ctx, "prependListener", ILibDuktape_EventEmitter_on, 2, 2, "once", duk_push_int_ex(ctx, 0), "prepend", duk_push_int_ex(ctx, 1));
ILibDuktape_CreateInstanceMethod(ctx, "eventNames", ILibDuktape_EventEmitter_eventNames, 0);
ILibDuktape_CreateInstanceMethod(ctx, "listenerCount", ILibDuktape_EventEmitter_listenerCount, 1);
ILibDuktape_CreateInstanceMethod(ctx, "listeners", ILibDuktape_EventEmitter_listeners, 1);
ILibDuktape_EventEmitter_CreateEventEx(retVal, "removeListener");
ILibDuktape_CreateProperty_InstanceMethod(ctx, "removeListener", ILibDuktape_EventEmitter_removeListener, 2);
ILibDuktape_CreateInstanceMethod(ctx, "removeAllListeners", ILibDuktape_EventEmitter_removeAllListeners, DUK_VARARGS);
ILibDuktape_CreateInstanceMethod(ctx, "emit", ILibDuktape_EventEmitter_emit, DUK_VARARGS);
ILibDuktape_CreateInstanceMethod(ctx, "emit_returnValue", ILibDuktape_EventEmitter_emitReturnValue, DUK_VARARGS);
ILibDuktape_EventEmitter_CreateEventEx(retVal, "~");
duk_push_c_function(ctx, ILibDuktape_EventEmitter_EmbeddedFinalizer, 1);
duk_set_finalizer(ctx, -2);
ILibDuktape_EventEmitter_CreateEventEx(retVal, "newListener");
ILibDuktape_EventEmitter_CreateEventEx(retVal, "newListener2");
duk_events_setup_on(ctx, -1, "newListener2", ILibDuktape_EventEmitter_listeners_tableinit); // [on][this][newListener][func]
duk_push_int(ctx, 1); duk_put_prop_string(ctx, -2, "add");
duk_push_true(ctx); duk_put_prop_string(ctx, -2, ILibDuktape_EventEmitter_InfrastructureEvent);
duk_pcall_method(ctx, 2); duk_pop(ctx); // ...
duk_events_setup_on(ctx, -1, "removeListener", ILibDuktape_EventEmitter_listeners_tableinit); // [on][this][removeListener][func]
duk_push_int(ctx, 0); duk_put_prop_string(ctx, -2, "add");
duk_push_true(ctx); duk_put_prop_string(ctx, -2, ILibDuktape_EventEmitter_InfrastructureEvent);
duk_pcall_method(ctx, 2); duk_pop(ctx); // ...
return retVal;
}
duk_ret_t ILibDuktape_EventEmitter_AddHookSink(duk_context *ctx)
{
duk_push_this(ctx); // [object]
duk_push_current_function(ctx); // [object][func]
char *eventName = Duktape_GetStringPropertyValue(ctx, -1, "eventName", "");
if (strcmp(eventName, duk_require_string(ctx, 0)) == 0)
{
ILibDuktape_EventEmitter_HookHandler handler = (ILibDuktape_EventEmitter_HookHandler)Duktape_GetPointerProperty(ctx, -1, "handler");
ILibDuktape_EventEmitter *emitter = (ILibDuktape_EventEmitter*)Duktape_GetBufferProperty(ctx, -2, ILibDuktape_EventEmitter_Data);
if (handler != NULL) { handler(emitter, eventName, duk_require_heapptr(ctx, 1)); }
}
return(0);
}
void ILibDuktape_EventEmitter_AddHook(ILibDuktape_EventEmitter *emitter, char *eventName, ILibDuktape_EventEmitter_HookHandler handler)
{
if (ILibMemory_CanaryOK(emitter) && duk_ctx_is_alive(emitter->ctx))
{
duk_push_heapptr(emitter->ctx, emitter->object); // [object]
duk_prepare_method_call(emitter->ctx, -1, "on"); // [object][on][this]
duk_push_string(emitter->ctx, "newListener"); // [object][on][this][newListener]
duk_push_c_function(emitter->ctx, ILibDuktape_EventEmitter_AddHookSink, DUK_VARARGS);// [object][on][this][newListener][func]
duk_push_string(emitter->ctx, eventName); duk_put_prop_string(emitter->ctx, -2, "eventName");
duk_push_pointer(emitter->ctx, handler); duk_put_prop_string(emitter->ctx, -2, "handler");
if (duk_pcall_method(emitter->ctx, 2) != 0) { ILibDuktape_Process_UncaughtExceptionEx(emitter->ctx, "events.addHook() Error"); }
duk_pop_2(emitter->ctx); // ...
}
}
void ILibDuktape_EventEmitter_ClearHook(ILibDuktape_EventEmitter *emitter, char *eventName)
{
if (ILibMemory_CanaryOK(emitter) && duk_ctx_is_alive(emitter->ctx))
{
duk_push_heapptr(emitter->ctx, emitter->object); // [object]
duk_prepare_method_call(emitter->ctx, -1, "listeners"); // [object][listeners][this]
duk_push_string(emitter->ctx, "newListener"); // [object][listeners][this][newListener]
if (duk_pcall_method(emitter->ctx, 1) == 0) // [object][array/error]
{
while (duk_get_length(emitter->ctx, -1) > 0)
{
duk_array_pop(emitter->ctx, -1); // [object][array][func]
if (strcmp(eventName, Duktape_GetStringPropertyValue(emitter->ctx, -1, "eventName", "")) == 0)
{
duk_prepare_method_call(emitter->ctx, -3, "removeListener");// [object][array][func][removeListener][this]
duk_push_string(emitter->ctx, "newListener"); // [object][array][func][removeListener][this][newListener]
duk_dup(emitter->ctx, -4); // [object][array][func][removeListener][this][newListener][func]
duk_pcall_method(emitter->ctx, 2); duk_pop(emitter->ctx); // [object][array][func]
}
duk_pop(emitter->ctx); // [object][array]
}
}
duk_pop_2(emitter->ctx); // ...
}
}
duk_ret_t ILibDuktape_EventEmitter_SetEvent(duk_context *ctx)
{
char *propName;
duk_size_t propNameLen;
duk_push_current_function(ctx); // [func]
duk_get_prop_string(ctx, -1, "eventName"); // [func][name]
propName = (char*)duk_get_lstring(ctx, -1, &propNameLen);
duk_push_this(ctx); // [obj]
if (duk_is_null_or_undefined(ctx, 0))
{
// NULL was passed, we'll need to clear all listeners.
duk_prepare_method_call(ctx, -1, "removeAllListeners"); // [obj][removeAllListeners][this]
duk_push_string(ctx, propName); // [obj][removeAllListeners][this][eventName]
duk_call_method(ctx, 1); // [obj][ret]
}
else
{
// Hook new event
duk_prepare_method_call(ctx, -1, "on"); // [obj][on][this]
duk_push_string(ctx, propName); // [obj][on][this][eventName]
duk_dup(ctx, 0); // [obj][on][this][eventName][handler]
duk_call_method(ctx, 2);
}
return(0);
//char *propName;
//duk_size_t propNameLen;
//ILibDuktape_EventEmitter *data;
//ILibLinkedList eventList = NULL;
//duk_push_current_function(ctx); // [func]
//duk_get_prop_string(ctx, -1, "eventName"); // [func][name]
//propName = (char*)duk_get_lstring(ctx, -1, &propNameLen);
//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.
// duk_push_this(ctx); // [obj]
// duk_get_prop_string(ctx, -1, "removeAllListeners"); // [obj][removeAll]
// duk_swap_top(ctx, -2); // [removeAll][this]
// duk_push_string(ctx, propName); // [removeAll][this][name]
// duk_call_method(ctx, 1); duk_pop(ctx);
//}
//else
//{
// ILibDuktape_EventEmitter_AddOn(data, propName, duk_get_heapptr(ctx, 0));
//}
//return 0;
}
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_CreateEventEx(ILibDuktape_EventEmitter *emitter, char *eventName)
{
duk_push_heapptr(emitter->ctx, emitter->object); // [obj]
duk_get_prop_string(emitter->ctx, -1, ILibDuktape_EventEmitter_EventTable); // [obj][table]
if (duk_has_prop_string(emitter->ctx, -1, eventName) == 0)
{
duk_push_array(emitter->ctx); duk_put_prop_string(emitter->ctx, -2, eventName);
duk_pop(emitter->ctx); // [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, "eventName"); // [obj][prop][setFunc]
duk_def_prop(emitter->ctx, -3, DUK_DEFPROP_SET_ENUMERABLE | DUK_DEFPROP_FORCE | DUK_DEFPROP_HAVE_SETTER); // [obj]
duk_pop(emitter->ctx); // ...
}
else
{
// Already Exists
duk_pop_2(emitter->ctx); // ...
}
}
duk_ret_t ILibDuktape_EventEmitter_Inherits_createEvent(duk_context *ctx)
{
char *name = (char*)duk_require_string(ctx, 0);
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_pop(ctx); // [emitterUtils]
ILibDuktape_EventEmitter_CreateEventEx(emitter, name);
return(1);
}
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));
duk_push_this(ctx);
return(1);
}
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]
ILibDuktape_CreateReadonlyProperty_SetEnumerable(ctx, ILibDuktape_EventEmitter_EmitterUtils, 0);
duk_get_prop_string(ctx, -1, ILibDuktape_EventEmitter_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;
}
duk_ret_t ILibDuktape_EventEmitter_EventEmitter(duk_context *ctx)
{
ILibDuktape_EventEmitter *emitter;
int nargs = duk_get_top(ctx);
int retVal = 0;
duk_push_this(ctx); // [target]
emitter = ILibDuktape_EventEmitter_Create(ctx);
duk_push_object(ctx); // [target][emitterUtils]
ILibDuktape_CreateReadonlyProperty_SetEnumerable(ctx, ILibDuktape_EventEmitter_EmitterUtils, 0);
duk_get_prop_string(ctx, -1, ILibDuktape_EventEmitter_EmitterUtils);
duk_push_pointer(ctx, emitter); // [target][emitterUtils][ptr]
duk_put_prop_string(ctx, -2, "emitter"); // [target][emitterUtils]
if (nargs == 1 && duk_require_boolean(ctx, 0))
{
// Explicit Events
ILibDuktape_CreateInstanceMethod(ctx, "createEvent", ILibDuktape_EventEmitter_Inherits_createEvent, 1);
ILibDuktape_CreateInstanceMethod(ctx, "addMethod", ILibDuktape_EventEmitter_Inherits_addMethod, 2);
retVal = 1;
}
else
{
// Implicit Events
emitter->eventType = ILibDuktape_EventEmitter_Type_IMPLICIT;
}
return(retVal);
}
duk_ret_t ILibDuktape_EventEmitter_moderated_impl(duk_context *ctx)
{
int nargs = duk_get_top(ctx);
if(nargs == 0) { return(0); } // NOP if evented value is undefined
int ok = 0, i;
duk_push_current_function(ctx); // [function]
uint32_t mval = (uint32_t)Duktape_GetIntPropertyValue(ctx, -1, "interval", 0);
if (mval != 0 && duk_has_prop_string(ctx, -1, "time"))
{
if (((uint32_t)ILibGetTimeStamp() - (uint32_t)Duktape_GetIntPropertyValue(ctx, -1, "time", 0)) < mval) { return(0); }
}
duk_get_prop_string(ctx, -1, "last"); // [function][array]
if (duk_get_length(ctx, -1) == nargs)
{
for (i = 0; i < nargs; ++i)
{
duk_get_prop_index(ctx, -1, (duk_uarridx_t)i); // [function][array][last]
duk_dup(ctx, (duk_idx_t)i); // [function][array][last][current]
ok = duk_strict_equals(ctx, -2, -1);
duk_pop_2(ctx); // [function][array]
if (ok == 0) { break; }
}
}
if (ok == 0)
{
// Update the Last Values with new Values
duk_push_current_function(ctx); // [function]
duk_push_uint(ctx, (duk_uint_t)ILibGetTimeStamp()); duk_put_prop_string(ctx, -2, "time");
duk_push_array(ctx); // [function][array]
for (i = 0; i < nargs; ++i)
{
duk_dup(ctx, i); // [function][array][val]
duk_put_prop_index(ctx, -2, (duk_uarridx_t)i); // [function][array]
}
// emit the event
duk_put_prop_string(ctx, -2, "last"); // [function]
duk_get_prop_string(ctx, -1, "func"); // [function][func]
duk_push_this(ctx); // [function][func][this]
for (i = 0; i < nargs; ++i)
{
duk_dup(ctx, i); // [function][func][this][...]
}
duk_call_method(ctx, nargs);
}
return(0);
}
duk_ret_t ILibDuktape_EventEmitter_moderated(duk_context *ctx)
{
if (!duk_is_function(ctx, 0)) { return(ILibDuktape_Error(ctx, "Argument Error")); }
duk_push_c_function(ctx, ILibDuktape_EventEmitter_moderated_impl, DUK_VARARGS); // [func]
duk_push_array(ctx); duk_put_prop_string(ctx, -2, "last"); // [func]
duk_dup(ctx, 0); duk_put_prop_string(ctx, -2, "func"); // [func]
duk_push_int(ctx, duk_is_number(ctx, 1) ? duk_require_int(ctx, 1) : 0); // [func][val]
duk_put_prop_string(ctx, -2, "interval"); // [func]
return(1);
}
duk_ret_t ILibDuktape_EventEmitter_allProperties(duk_context *ctx)
{
duk_push_current_function(ctx);
int all = Duktape_GetIntPropertyValue(ctx, -1, "all", 0);
const char *tmp;
duk_push_array(ctx); // [arr]
duk_dup(ctx, 0); // [arr][obj]
duk_enum(ctx, -1, (all == 0 ? 0 : DUK_ENUM_INCLUDE_NONENUMERABLE) | (all == 2 ? DUK_ENUM_OWN_PROPERTIES_ONLY : 0) | DUK_ENUM_INCLUDE_HIDDEN | DUK_ENUM_INCLUDE_SYMBOLS);;
while (duk_next(ctx, -1, 0)) // [arr][obj][enum][key]
{
tmp = NULL;
if (duk_is_symbol(ctx, -1))
{
duk_size_t len;
tmp = Duktape_GetBuffer(ctx, -1, &len);
char *buf = duk_push_fixed_buffer(ctx, len + 1); // [arr][obj][enum][key][buf]
duk_insert(ctx, -5); // [buf][arr][obj][enum][key]
memcpy_s(buf, len + 1, tmp, len);
buf[0] = '?';
buf[len] = 0;
tmp = buf;
if (all == 0)
{
if (strcmp(tmp, "?Finalizer") == 0) { tmp = NULL; }
}
}
else
{
if (all) { tmp = duk_get_string(ctx, -1); }
}
if (tmp != NULL)
{
duk_push_string(ctx, tmp); // [arr][obj][enum][key][string]
duk_array_push(ctx, -5); // [arr][obj][enum][key]
}
duk_pop(ctx); // [arr][obj][enum]
}
duk_pop_2(ctx); // [arr]
return(1);
}
duk_ret_t ILibDuktape_EventEmitter_showReferences(duk_context *ctx)
{
const char *ID, *MSG;
duk_push_heap_stash(ctx); // [stash]
duk_get_prop_string(ctx, -1, ILibDuktape_EventEmitter_References); // [stash][refs]
if (!duk_is_null_or_undefined(ctx, -1))
{
duk_enum(ctx, -1, DUK_ENUM_OWN_PROPERTIES_ONLY); // [stash][refs][enum]
while (duk_next(ctx, -1, 1)) // [stash][refs][enum][key][value]
{
ID = Duktape_GetStringPropertyValue(ctx, -1, ILibDuktape_OBJID, "?");
MSG = Duktape_GetStringPropertyValue(ctx, -1, ILibDuktape_EventEmitter_FinalizerDebugMessage, "");
duk_push_global_object(ctx); // [stash][refs][enum][key][value][g]
duk_get_prop_string(ctx, -1, "console"); // [stash][refs][enum][key][value][g][console]
duk_remove(ctx, -2); // [stash][refs][enum][key][value][console]
duk_prepare_method_call(ctx, -1, "log"); // [stash][refs][enum][key][value][console][log][this]
duk_push_sprintf(ctx, "%s%s%s", ID, MSG != NULL ? " => " : "", MSG != NULL ? MSG : ""); //][value][console][log][this][string]
duk_call_method(ctx, 1); duk_pop_n(ctx, 2); // [stash][refs][enum][key][value]
duk_prepare_method_call(ctx, -1, "eventNames"); // [stash][refs][enum][key][value][eventNames][this]
if (duk_pcall_method(ctx, 0) == 0) // [stash][refs][enum][key][value][array]
{
if (duk_get_length(ctx, -1) > 0)
{
duk_push_global_object(ctx); // [stash][refs][enum][key][value][array][g]
duk_get_prop_string(ctx, -1, "JSON"); // [stash][refs][enum][key][value][array][g][JSON]
duk_prepare_method_call(ctx, -1, "stringify"); // [stash][refs][enum][key][value][array][g][JSON][stringify][this]
duk_dup(ctx, -5); // [stash][refs][enum][key][value][array][g][JSON][stringify][this][array]
duk_call_method(ctx, 1); // [stash][refs][enum][key][value][array][g][JSON][STRING]
duk_get_prop_string(ctx, -3, "console"); // [stash][refs][enum][key][value][array][g][JSON][STRING][console]
duk_prepare_method_call(ctx, -1, "log"); // [stash][refs][enum][key][value][array][g][JSON][STRING][console][log][this]
duk_push_sprintf(ctx, " -> events %s", duk_get_string(ctx, -4));// tash][refs][enum][key][value][array][g][JSON][STRING][console][log][this][VAL]
duk_call_method(ctx, 1); // [stash][refs][enum][key][value][array][g][JSON][STRING][console][ret]
duk_pop_n(ctx, 5); // [stash][refs][enum][key][value][array]
}
duk_eval_string(ctx, "require('events')"); // [stash][refs][enum][key][value][array][events]
duk_prepare_method_call(ctx, -1, "hiddenProperties"); // [stash][refs][enum][key][value][array][events][hidden][this]
duk_dup(ctx, -5); // [stash][refs][enum][key][value][array][events][hidden][this][value]
duk_call_method(ctx, 1); // [stash][refs][enum][key][value][array][events][props]
duk_push_global_object(ctx); // [stash][refs][enum][key][value][array][events][props][g]
duk_get_prop_string(ctx, -1, "JSON"); // [stash][refs][enum][key][value][array][events][props][g][JSON]
duk_prepare_method_call(ctx, -1, "stringify"); // [stash][refs][enum][key][value][array][events][props][g][JSON][stringify][this]
duk_dup(ctx, -5); // [stash][refs][enum][key][value][array][events][props][g][JSON][stringify][this][props]
duk_push_null(ctx); duk_push_int(ctx, 1); // [stash][refs][enum][key][value][array][events][props][g][JSON][stringify][this][props][null][1]
duk_call_method(ctx, 3); // [stash][refs][enum][key][value][array][events][props][g][JSON][PROPSTRING]
duk_get_prop_string(ctx, -3, "console"); // [stash][refs][enum][key][value][array][events][props][g][JSON][PROPSTRING][console]
duk_prepare_method_call(ctx, -1, "log"); // [stash][refs][enum][key][value][array][events][props][g][JSON][PROPSTRING][console][log][this]
duk_push_sprintf(ctx, " -> props %s", duk_get_string(ctx, -4)); // [refs][enum][key][value][array][events][props][g][JSON][PROPSTRING][console][log][this][string]
duk_call_method(ctx, 1); // [stash][refs][enum][key][value][array][events][props][g][JSON][PROPSTRING][console][ret]
duk_pop_n(ctx, 7); // [stash][refs][enum][key][value][array]
}
duk_pop_n(ctx, 3); // [stash][refs][enum]
}
}
return(0);
}
duk_ret_t ILibDuktape_EventEmitter_addHidden(duk_context *ctx)
{
duk_dup(ctx, 0); // [a]
duk_dup(ctx, 1); // [a][b]
duk_put_prop_string(ctx, -2, "\xFF_HIDDEN");
return(0);
}
duk_ret_t ILibDuktape_EventEmitter_deleteProperty(duk_context *ctx)
{
char *tmp;
duk_dup(ctx, 0); // [obj]
if (duk_is_string(ctx, 1))
{
duk_dup(ctx, 1); // [obj][string]
duk_del_prop(ctx, -2);
}
else if (duk_is_array(ctx, 1))
{
duk_dup(ctx, 1); // [obj][array]
while (duk_get_length(ctx, -1) > 0)
{
duk_array_pop(ctx, -1); // [obj][array][string]
tmp = (char*)duk_push_sprintf(ctx, "%s", duk_get_string(ctx, -1)); // [obj][array][string][string]
tmp[0] = '\xFF';
duk_del_prop_string(ctx, -4, tmp);
duk_pop_2(ctx); // [obj][array]
}
}
else
{
return(ILibDuktape_Error(ctx, "Invalid Args"));
}
return(0);
}
duk_ret_t ILibDuktape_EventEmitter_setFinalizerMetadata(duk_context *ctx)
{
duk_push_this(ctx);
duk_dup(ctx, 0);
duk_put_prop_string(ctx, -2, ILibDuktape_EventEmitter_FinalizerDebugMessage);
return(0);
}
duk_ret_t ILibDuktape_RefCountPointer_eval(duk_context *ctx)
{
duk_push_this(ctx);
duk_int_t *t = (duk_int_t*)Duktape_GetPointerProperty(ctx, -1, "_ptr");
if (t == NULL) { return(ILibDuktape_Error(ctx, "ERROR")); }
duk_push_int(ctx, *t);
return(1);
}
duk_ret_t ILibDuktape_RefCountPointer_set(duk_context *ctx)
{
duk_push_this(ctx);
duk_int_t *t = (duk_int_t*)Duktape_GetPointerProperty(ctx, -1, "_ptr");
if (t == NULL) { return(ILibDuktape_Error(ctx, "ERROR")); }
*t = duk_require_int(ctx, 0);
return(0);
}
duk_ret_t ILibDuktape_RefCountPointer(duk_context *ctx)
{
int nargs = duk_get_top(ctx);
duk_push_this(ctx);
char *tmp = Duktape_GetStringPropertyValue(ctx, -1, ILibDuktape_OBJID, "");
if (nargs == 1) { duk_dup(ctx, 0); }
duk_int_t *t = _get_refcount_ptr(ctx, -1);
if (nargs == 1) { duk_pop(ctx); }
ILibDuktape_GenericMarshal_Variable_PUSH(ctx, t, sizeof(void*));
duk_push_sprintf(ctx, "_get_refcount_ptr(%s)", tmp); duk_put_prop_string(ctx, -2, ILibDuktape_EventEmitter_FinalizerDebugMessage);
ILibDuktape_CreateInstanceMethod(ctx, "eval", ILibDuktape_RefCountPointer_eval, 0);
ILibDuktape_CreateInstanceMethod(ctx, "set", ILibDuktape_RefCountPointer_set, 1);
return(1);
}
duk_ret_t ILibDuktape_EventEmitter_getProperty(duk_context *ctx)
{
duk_push_this(ctx); // [obj]
char *tmp = (char*)duk_push_sprintf(ctx, "%s", duk_get_string(ctx, 0));// [obj][string]
if (tmp[0] == '?') { tmp[0] = '\xFF'; }
duk_get_prop_string(ctx, -2, tmp);
tmp = (char*)duk_get_string(ctx, -1);
return(1);
}
duk_ret_t ILibDuktape_EventEmitter_setProperty(duk_context *ctx)
{
duk_push_this(ctx); // [obj]
char *tmp = (char*)duk_push_sprintf(ctx, "%s", duk_get_string(ctx, 0));// [obj][string]
duk_dup(ctx, 1); // [obj][string][val]
if (tmp[0] == '?') { tmp[0] = '\xFF'; }
duk_put_prop_string(ctx, -3, tmp);
return(0);
}
void ILibDuktape_EventEmitter_PUSH(duk_context *ctx, void *chain)
{
duk_push_object(ctx); // [emitter]
ILibDuktape_CreateInstanceMethod(ctx, "inherits", ILibDuktape_EventEmitter_Inherits, 1);
ILibDuktape_CreateInstanceMethod(ctx, "EventEmitter", ILibDuktape_EventEmitter_EventEmitter, DUK_VARARGS);
ILibDuktape_CreateInstanceMethod(ctx, "moderated", ILibDuktape_EventEmitter_moderated, DUK_VARARGS);
ILibDuktape_CreateInstanceMethodWithIntProperty(ctx, "all", 1, "allProperties", ILibDuktape_EventEmitter_allProperties, 1);
ILibDuktape_CreateInstanceMethodWithIntProperty(ctx, "all", 0, "hiddenProperties", ILibDuktape_EventEmitter_allProperties, 1);
ILibDuktape_CreateInstanceMethodWithIntProperty(ctx, "all", 2, "allOwnProperties", ILibDuktape_EventEmitter_allProperties, 1);
ILibDuktape_CreateInstanceMethod(ctx, "showReferences", ILibDuktape_EventEmitter_showReferences, DUK_VARARGS);
ILibDuktape_CreateInstanceMethod(ctx, "addHiddenReference", ILibDuktape_EventEmitter_addHidden, 2);
ILibDuktape_CreateInstanceMethod(ctx, "deleteProperty", ILibDuktape_EventEmitter_deleteProperty, 2);
ILibDuktape_CreateInstanceMethod(ctx, "setFinalizerMetadata", ILibDuktape_EventEmitter_setFinalizerMetadata, 1);
ILibDuktape_CreateInstanceMethod(ctx, "_refCountPointer", ILibDuktape_RefCountPointer, DUK_VARARGS);
ILibDuktape_CreateInstanceMethod(ctx, "getProperty", ILibDuktape_EventEmitter_getProperty, 1);
ILibDuktape_CreateInstanceMethod(ctx, "setProperty", ILibDuktape_EventEmitter_setProperty, 2);
}
void ILibDuktape_EventEmitter_Init(duk_context *ctx)
{
ILibDuktape_ModSearch_AddHandler(ctx, "events", ILibDuktape_EventEmitter_PUSH);
}
duk_ret_t ILibDuktape_EventEmitter_ForwardEvent_Sink(duk_context *ctx)
{
int nargs = duk_get_top(ctx);
int i;
char *name;
duk_push_current_function(ctx); // [func]
duk_get_prop_string(ctx, -1, "targetObject"); // [func][obj]
duk_get_prop_string(ctx, -1, "emit"); // [func][obj][emit]
duk_swap_top(ctx, -2); // [func][emit][this]
duk_get_prop_string(ctx, -3, "targetName"); // [func][emit][this][name]
name = (char*)duk_get_string(ctx, -1);
for (i = 0; i < nargs; ++i)
{
duk_dup(ctx, i); // [func][emit][this][name][...args...]
}
if (duk_pcall_method(ctx, 1 + nargs) != 0) { return(ILibDuktape_Error(ctx, "EventEmitter.ForwardEvent() [%s]: %s", name, duk_safe_to_string(ctx, -1))); }
return(0);
}
duk_ret_t ILibDuktape_EventEmitter_ForwardEvent_Finalizer(duk_context *ctx)
{
void *src = NULL;
char *srcName = NULL;
if (g_displayFinalizerMessages)
{
duk_push_this(ctx);
src = duk_get_heapptr(ctx, -1);
srcName = Duktape_GetStringPropertyValue(ctx, -1, ILibDuktape_OBJID, "UNKNOWN");
duk_pop(ctx);
}
duk_push_current_function(ctx); // [func]
if (duk_has_prop_string(ctx, -1, "fptr"))
{
duk_get_prop_string(ctx, -1, "fptr"); // [func][fptr]
if (duk_has_prop_string(ctx, -1, "targetObject"))
{
duk_get_prop_string(ctx, -1, "targetObject"); // [func][fptr][target]
duk_del_prop_string(ctx, -2, "targetObject");
if (g_displayFinalizerMessages) { printf("EventEmitter.Forwarder[%s]: Deleted reference to [%s/%p] RC=%d from [%s/%p]\n", Duktape_GetStringPropertyValue(ctx, -3, "targetName", "UNKNOWN"), Duktape_GetStringPropertyValue(ctx, -1, ILibDuktape_OBJID, "UNKNOWN"), duk_get_heapptr(ctx, -1), ILibDuktape_GetReferenceCount(ctx, -1) - 1, srcName, src); }
duk_pop_n(ctx, 3);
}
}
if (g_displayFinalizerMessages) { duk_eval_string(ctx, "_debugGC();"); duk_pop(ctx); }
return(0);
}
duk_ret_t ILibDuktape_EventEmitter_ForwardEvent_HookSink(duk_context *ctx)
{
duk_push_current_function(ctx);
duk_size_t sourceLen, targetLen, hookLen;
char *source, *target, *hook;
void *sourceObject, *fptr;
source = Duktape_GetStringPropertyValueEx(ctx, -1, ILibDuktape_EventEmitter_Forward_SourceName, NULL, &sourceLen);
target = Duktape_GetStringPropertyValueEx(ctx, -1, ILibDuktape_EventEmitter_Forward_TargetName, NULL, &targetLen);
sourceObject = Duktape_GetHeapptrProperty(ctx, -1, ILibDuktape_EventEmitter_Forward_SourceObject);
if (source != NULL && target != NULL && sourceObject != NULL)
{
hook = (char*)duk_get_lstring(ctx, 0, &hookLen);
if (!(hookLen == targetLen && strncmp(target, hook, hookLen) == 0))
{
// This hooked event wasn't for us, so let's rehook this logic up for next time
duk_push_this(ctx); // [this]
duk_get_prop_string(ctx, -1, "once"); // [this][once]
duk_swap_top(ctx, -2); // [once][this]
duk_push_string(ctx, "newListener"); // [once][this][newListener]
duk_push_c_function(ctx, ILibDuktape_EventEmitter_ForwardEvent_HookSink, DUK_VARARGS); // [once][this][newListener][func]
duk_push_lstring(ctx, source, sourceLen); duk_put_prop_string(ctx, -2, ILibDuktape_EventEmitter_Forward_SourceName);
duk_push_lstring(ctx, target, targetLen); duk_put_prop_string(ctx, -2, ILibDuktape_EventEmitter_Forward_TargetName);
duk_push_heapptr(ctx, sourceObject); duk_put_prop_string(ctx, -2, ILibDuktape_EventEmitter_Forward_SourceObject);
duk_call_method(ctx, 2); duk_pop(ctx); // ...
}
else
{
// This hooked event is for us
ILibDuktape_EventEmitter_SetupOn(ctx, sourceObject, source); // [on][this][source]
duk_push_c_function(ctx, ILibDuktape_EventEmitter_ForwardEvent_Sink, DUK_VARARGS); // [on][this][source][sink]
fptr = duk_get_heapptr(ctx, -1);
duk_push_this(ctx); duk_put_prop_string(ctx, -2, "targetObject");
duk_push_lstring(ctx, target, targetLen); duk_put_prop_string(ctx, -2, "targetName");
if (duk_pcall_method(ctx, 2) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ctx, "EventEmitter_ForwardEvent(): "); }
duk_pop(ctx); // ...
ILibDuktape_EventEmitter_SetupPrependOnce(ctx, sourceObject, "~"); // [prependOnce][this][~]
duk_push_c_function(ctx, ILibDuktape_EventEmitter_ForwardEvent_Finalizer, DUK_VARARGS); // [prependOnce][this]['~'][func]
duk_push_heapptr(ctx, fptr); duk_put_prop_string(ctx, -2, "fptr");
duk_push_lstring(ctx, target, targetLen); duk_put_prop_string(ctx, -2, "targetName");
if (duk_pcall_method(ctx, 2) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ctx, "EventEmitter_ForwardEvent_SetFinalizer(): "); }
duk_pop(ctx); // ...
}
}
return(0);
}
void ILibDuktape_EventEmitter_DeleteForwardEvent(duk_context *ctx, duk_idx_t eventSourceIndex, char *sourceEventName)
{
duk_dup(ctx, eventSourceIndex); // [source]
if (duk_has_prop_string(ctx, -1, ILibDuktape_EventEmitter_ForwardTable))
{
duk_get_prop_string(ctx, -1, ILibDuktape_EventEmitter_ForwardTable); // [source][table]
if (duk_has_prop_string(ctx, -1, sourceEventName))
{
duk_get_prop_string(ctx, -1, sourceEventName); // [source][table][sink]
duk_del_prop_string(ctx, -1, "targetObject");
duk_get_prop_string(ctx, -3, "removeListener"); // [source][table][sink][removeListener]
duk_dup(ctx, -4); // [source][table][sink][removeListener][this]
duk_push_string(ctx, sourceEventName); // [source][table][sink][removeListener][this][name]
duk_dup(ctx, -4); // [source][table][sink][removeListener][this][name][sink]
duk_call_method(ctx, 2); duk_pop_2(ctx); // [source][table]
if (duk_has_prop_string(ctx, -1, "~"))
{
duk_get_prop_string(ctx, -1, "~"); // [source][table][sink]
duk_del_prop_string(ctx, -1, "fptr");
duk_get_prop_string(ctx, -3, "removeListener"); // [source][table][sink][removeListener]
duk_dup(ctx, -4); // [source][table][sink][removeListener][this]
duk_push_string(ctx, "~"); // [source][table][sink][removeListener][this][name]
duk_dup(ctx, -4); // [source][table][sink][removeListener][this][name][sink]
duk_call_method(ctx, 2); duk_pop_2(ctx); // [source][table]
}
if (duk_has_prop_string(ctx, -1, "newListener"))
{
duk_get_prop_string(ctx, -1, "newListener"); // [source][table][sink]
duk_get_prop_string(ctx, -3, "removeListener"); // [source][table][sink][removeListener]
duk_dup(ctx, -4); // [source][table][sink][removeListener][this]
duk_push_string(ctx, "newListener"); // [source][table][sink][removeListener][this][name]
duk_dup(ctx, -4); // [source][table][sink][removeListener][this][name][sink]
duk_call_method(ctx, 2); duk_pop_2(ctx); // [source][table]
}
}
duk_pop(ctx); // [source]
}
duk_pop(ctx); // ...
}
void ILibDuktape_EventEmitter_ForwardEvent(duk_context *ctx, duk_idx_t eventSourceIndex, char *sourceEventName, duk_idx_t eventTargetIndex, char *targetEventName)
{
void *fptr;
void *source;
void *target;
void *table = NULL;
duk_dup(ctx, eventTargetIndex); // [targetObject]
target = duk_get_heapptr(ctx, -1);
duk_pop(ctx); // ...
duk_dup(ctx, eventSourceIndex); // [sourceObject]
source = duk_get_heapptr(ctx, -1);
duk_pop(ctx); // ...
duk_push_heapptr(ctx, source); // [source]
ILibDuktape_EventEmitter_DeleteForwardEvent(ctx, -1, sourceEventName);
if (duk_has_prop_string(ctx, -1, ILibDuktape_EventEmitter_ForwardTable))
{
duk_get_prop_string(ctx, -1, ILibDuktape_EventEmitter_ForwardTable); // [source][table]
table = duk_get_heapptr(ctx, -1);
duk_pop(ctx); // [source]
}
else
{
duk_push_object(ctx); // [source][table]
table = duk_get_heapptr(ctx, -1);
duk_put_prop_string(ctx, -2, ILibDuktape_EventEmitter_ForwardTable); // [source]
}
duk_pop(ctx); // ...
duk_push_heapptr(ctx, target); // [target]
if (ILibDuktape_EventEmitter_HasListeners(ILibDuktape_EventEmitter_GetEmitter(ctx, -1), targetEventName) > 0)
{
// Target already has listeners, so we can go ahead and forward events
duk_pop(ctx); // ...
ILibDuktape_EventEmitter_SetupOn(ctx, source, sourceEventName); // [on][this][source]
duk_push_c_function(ctx, ILibDuktape_EventEmitter_ForwardEvent_Sink, DUK_VARARGS); // [on][this][source][sink]
fptr = duk_get_heapptr(ctx, -1);
duk_push_heapptr(ctx, target); duk_put_prop_string(ctx, -2, "targetObject");
duk_push_string(ctx, targetEventName); duk_put_prop_string(ctx, -2, "targetName");
if (duk_pcall_method(ctx, 2) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ctx, "EventEmitter_ForwardEvent(): "); }
duk_pop(ctx); // ...
duk_push_heapptr(ctx, table); // [table]
duk_push_heapptr(ctx, fptr); // [table][func]
duk_put_prop_string(ctx, -2, sourceEventName); // [table]
duk_pop(ctx); // ...
ILibDuktape_EventEmitter_SetupPrependOnce(ctx, source, "~"); // [prependOnce][this][~]
duk_push_c_function(ctx, ILibDuktape_EventEmitter_ForwardEvent_Finalizer, DUK_VARARGS); // [prependOnce][this]['~'][func]
duk_push_heapptr(ctx, table); // [prependOnce][this]['~'][func][table]
duk_dup(ctx, -2); // [prependOnce][this]['~'][func][table][func]
duk_put_prop_string(ctx, -2, "~"); // [prependOnce][this]['~'][func][table]
duk_pop(ctx); // [prependOnce][this]['~'][func]
duk_push_heapptr(ctx, fptr); duk_put_prop_string(ctx, -2, "fptr");
duk_push_string(ctx, targetEventName); duk_put_prop_string(ctx, -2, "targetName");
if (duk_pcall_method(ctx, 2) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ctx, "EventEmitter_ForwardEvent_SetFinalizer(): "); }
duk_pop(ctx); // ...
}
else
{
// Target has no listeners, so only forward events if someone adds a listener
duk_get_prop_string(ctx, -1, "once"); // [target][once]
duk_swap_top(ctx, -2); // [once][this]
duk_push_string(ctx, "newListener"); // [once][this][newListener]
duk_push_c_function(ctx, ILibDuktape_EventEmitter_ForwardEvent_HookSink, DUK_VARARGS); // [once][this][newListener][func]
duk_push_string(ctx, sourceEventName); duk_put_prop_string(ctx, -2, ILibDuktape_EventEmitter_Forward_SourceName);
duk_push_string(ctx, targetEventName); duk_put_prop_string(ctx, -2, ILibDuktape_EventEmitter_Forward_TargetName);
duk_push_heapptr(ctx, source); duk_put_prop_string(ctx, -2, ILibDuktape_EventEmitter_Forward_SourceObject);
if (duk_pcall_method(ctx, 2) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ctx, "Error hooking event: %s ", targetEventName); }
duk_pop(ctx); // ...
}
}
int ILibDuktape_EventEmitter_AddOnEx(duk_context *ctx, duk_idx_t idx, char *eventName, duk_c_function func)
{
int retVal = 1;
duk_dup(ctx, idx); // [object]
duk_get_prop_string(ctx, -1, "on"); // [object][on]
duk_swap_top(ctx, -2); // [on][this]
duk_push_string(ctx, eventName); // [on][this][name]
duk_push_c_function(ctx, func, DUK_VARARGS); // [on][this][name][func]
if (duk_pcall_method(ctx, 2) != 0) { retVal = 0; }
duk_pop(ctx); // ...
return(retVal);
}
duk_ret_t ILibDuktape_EventEmitter_ForwardEx_target_newListenerSink(duk_context *ctx)
{
duk_push_current_function(ctx); // [func]
char *eventName = Duktape_GetStringPropertyValue(ctx, -1, "eventName", NULL);
void **ptr = (void**)Duktape_GetPointerProperty(ctx, -1, "sourcePtr");
char *hookedEvent = (char*)duk_require_string(ctx, 0);
if (eventName == NULL || !ILibMemory_CanaryOK(ptr) || strcmp(eventName, hookedEvent) != 0) { return(0); }
duk_dup(ctx, 1); // [func]
duk_prepare_method_call(ctx, -1, "bind"); // [func][bind][this]
duk_push_this(ctx); // [func][bind][this][target]
duk_call_method(ctx, 1); // [func][proxyFunc]
duk_push_true(ctx); duk_put_prop_string(ctx, -2, ILibDuktape_EventEmitter_InfrastructureEvent);
duk_push_pointer(ctx, ptr); duk_put_prop_string(ctx, -2, "sourcePtr");
duk_dup(ctx, -1); // [func][proxyFunc][proxyFunc]
duk_put_prop_string(ctx, -3, "proxyFunc"); // [func][proxyFunc]
duk_push_heapptr(ctx, ptr[0]); // [func][proxyFunc][source]
duk_prepare_method_call(ctx, -1, "on"); // [func][proxyFunc][source][on][this]
duk_dup(ctx, 0); // [func][proxyFunc][source][on][this][eventName]
duk_dup(ctx, -5); // [func][proxyFunc][source][on][this][eventName][func]
duk_call_method(ctx, 2); // [func][proxyFunc][source][ret]
return(0);
}
duk_ret_t ILibDuktape_EventEmitter_ForwardEx_target_removeListenerSink(duk_context *ctx)
{
duk_push_current_function(ctx); // [func]
char *eventName = Duktape_GetStringPropertyValue(ctx, -1, "eventName", NULL);
void **ptr = (void**)Duktape_GetPointerProperty(ctx, -1, "sourcePtr");
char *hookedEvent = (char*)duk_require_string(ctx, 0);
if (eventName == NULL || !ILibMemory_CanaryOK(ptr) || strcmp(eventName, hookedEvent) != 0 || !duk_has_prop_string(ctx, 1, "proxyFunc")) { return(0); }
duk_push_heapptr(ctx, ptr[0]); // [source]
duk_prepare_method_call(ctx, -1, "removeListener"); // [source][removeListener][this]
duk_dup(ctx, 0); // [source][removeListener][this][name]
duk_get_prop_string(ctx, 1, "proxyFunc"); // [source][removeListener][this][name][func]
duk_call_method(ctx, 2); // [source][ret]
duk_del_prop_string(ctx, 1, "proxyFunc");
return(0);
}
int ILibDuktape_EventEmitter_ForwardEventEx(duk_context *ctx, duk_idx_t sourceIdx, duk_idx_t targetIdx, char *eventName)
{
int X = duk_get_top(ctx);
void *source = duk_get_heapptr(ctx, sourceIdx);
void *target = duk_get_heapptr(ctx, targetIdx);
void **ptr;
duk_push_heapptr(ctx, source); // [source]
if (!duk_has_prop_string(ctx, -1, "\xFF_ProxyEvent_WeakReference"))
{
ptr = (void**)Duktape_PushBuffer(ctx, sizeof(void*)); // [source][buffer]
duk_put_prop_string(ctx, -2, "\xFF_ProxyEvent_WeakReference"); // [source]
ptr[0] = source;
}
else
{
ptr = Duktape_GetPointerProperty(ctx, -1, "\xFF_ProxyEvent_WeakReference");
}
duk_pop(ctx); // ...
// Check for pre-existing event listeners
duk_prepare_method_call(ctx, targetIdx, "listeners"); // [listeners][this]
duk_push_string(ctx, eventName); // [listeners][this][name]
if (duk_pcall_method(ctx, 1) != 0) { duk_set_top(ctx, X); return(1); } // [array]
while(duk_get_length(ctx, -1)>0)
{
duk_array_pop(ctx, -1); // [array][func]
if (!duk_has_prop_string(ctx, -1, "proxyFunc"))
{
duk_prepare_method_call(ctx, -1, "bind"); // [array][func][bind][this]
duk_push_heapptr(ctx, target); // [array][func][bind][this][target]
if (duk_pcall_method(ctx, 1) != 0) { duk_set_top(ctx, X); return(1); } // [array][func][proxyFunc]
duk_push_true(ctx); duk_put_prop_string(ctx, -2, ILibDuktape_EventEmitter_InfrastructureEvent);
duk_push_pointer(ctx, ptr); duk_put_prop_string(ctx, -2, "sourcePtr");
duk_put_prop_string(ctx, -2, "proxyFunc"); // [array][func]
}
duk_push_heapptr(ctx, source); // [array][func][source]
duk_prepare_method_call(ctx, -1, "on"); // [array][func][source][on][this]
duk_push_string(ctx, eventName); // [array][func][source][on][this][eventName]
duk_get_prop_string(ctx, -5, "proxyFunc"); // [array][func][source][on][this][eventName][proxyFunc]
if (duk_pcall_method(ctx, 2) != 0) { duk_set_top(ctx, X); return(1); } // [array][func][source][ret]
duk_pop_n(ctx, 3); // [array]
}
// Hookup a 'newListener' hook, to attach new subscribers
int ret = 0;
duk_set_top(ctx, X);
duk_events_setup_on(ctx, targetIdx, "newListener", ILibDuktape_EventEmitter_ForwardEx_target_newListenerSink); // [on][this][newListener][func]
duk_push_string(ctx, eventName); duk_put_prop_string(ctx, -2, "eventName");
duk_push_pointer(ctx, ptr); duk_put_prop_string(ctx, -2, "sourcePtr");
duk_push_true(ctx); duk_put_prop_string(ctx, -2, ILibDuktape_EventEmitter_InfrastructureEvent);
ret = duk_pcall_method(ctx, 2) == 0 ? 0 : 1;
duk_set_top(ctx, X);
if (ret == 0)
{
// Hookup a 'removeListener' hook, to remove subscribers
duk_events_setup_on(ctx, targetIdx, "removeListener", ILibDuktape_EventEmitter_ForwardEx_target_removeListenerSink); // [on][this][removeListener][func]
duk_push_string(ctx, eventName); duk_put_prop_string(ctx, -2, "eventName");
duk_push_pointer(ctx, ptr); duk_put_prop_string(ctx, -2, "sourcePtr");
duk_push_true(ctx); duk_put_prop_string(ctx, -2, ILibDuktape_EventEmitter_InfrastructureEvent);
ret = duk_pcall_method(ctx, 2) == 0 ? 0 : 1;
duk_set_top(ctx, X);
}
return(ret);
}