diff --git a/examples/node-addon/core.js b/examples/node-addon/core.js index 64bb1a2..1752c6d 100644 --- a/examples/node-addon/core.js +++ b/examples/node-addon/core.js @@ -1,3 +1,3 @@ 'use strict'; -module.exports = require('./binary/example'); +module.exports = require('./binary/addon'); diff --git a/examples/node-addon/cpp/example.cpp b/examples/node-addon/cpp/example.cpp index dd51765..0fafe5b 100644 --- a/examples/node-addon/cpp/example.cpp +++ b/examples/node-addon/cpp/example.cpp @@ -28,12 +28,13 @@ void Example::init(Handle target) { // prototype Nan::SetPrototypeMethod(ctor, "destroy", destroy); + extend(ctor); + + Local proto = ctor->PrototypeTemplate(); // ACCESSOR_RW(proto, prop); - EventEmitter::extend(ctor, proto); - _constructor.Reset(Nan::GetFunction(ctor).ToLocalChecked()); Nan::Set(target, JS_STR("Example"), Nan::GetFunction(ctor).ToLocalChecked()); diff --git a/examples/node-addon/index.js b/examples/node-addon/index.js index dfff914..1c2e7a2 100644 --- a/examples/node-addon/index.js +++ b/examples/node-addon/index.js @@ -1,10 +1,46 @@ 'use strict'; -const Wrapped = require('./js/wrapped'); -const Example = require('./js/example'); +const Example = require('./core'); console.log('Example', Example); -console.log('Wrapped', Wrapped); + +const example = new Example(); + + +console.log('static listenerCount', Example.listenerCount); + +console.log('listenerCount', example.listenerCount); +console.log('addListener', example.addListener); +console.log('emit', example.emit); +console.log('eventNames', example.eventNames); +console.log('getMaxListeners', example.getMaxListeners); +console.log('listeners', example.listeners); +console.log('on', example.on); +console.log('once', example.once); +console.log('prependListener', example.prependListener); +console.log('prependOnceListener', example.prependOnceListener); +console.log('removeAllListeners', example.removeAllListeners); +console.log('removeListener', example.removeListener); +console.log('setMaxListeners', example.setMaxListeners); +console.log('rawListeners', example.rawListeners); +console.log('destroy', example.destroy); + + +example.on('evt1', (arg1, arg2) => { + console.log('EVT1', arg1, arg2); +}); + +example.once('evt2', (arg1, arg2) => { + console.log('EVT2', arg1, arg2); +}); + + +example.emit('evt1', 111, '221'); +example.emit('evt1', 112, '222'); + +example.emit('evt2', 111, '221'); +example.emit('evt2', 112, '222'); + module.exports = { Wrapped, Example }; diff --git a/examples/node-addon/js/example.js b/examples/node-addon/js/example.js deleted file mode 100644 index d3f20bd..0000000 --- a/examples/node-addon/js/example.js +++ /dev/null @@ -1,7 +0,0 @@ -'use strict'; - - -const { Example } = require('../core'); - - -module.exports = Example; diff --git a/examples/node-addon/js/wrapped.js b/examples/node-addon/js/wrapped.js deleted file mode 100644 index b28abb9..0000000 --- a/examples/node-addon/js/wrapped.js +++ /dev/null @@ -1,12 +0,0 @@ -'use strict'; - - -const { Example } = require('../core'); - - -class Wrapped extends Example { - -} - - -module.exports = Wrapped; diff --git a/include/event-emitter.hpp b/include/event-emitter.hpp index 62014af..d813055 100644 --- a/include/event-emitter.hpp +++ b/include/event-emitter.hpp @@ -12,7 +12,19 @@ EventEmitter *emitter = ObjectWrap::Unwrap(info.This()); -class EventEmitter : public Nan::ObjectWrap { +// Use the dummy template for static-int initialization + +template< class Dummy > +class EventEmitterStatics { +protected: + static int _defaultMaxListeners; +}; + +template< class Dummy > +int EventEmitterStatics::_defaultMaxListeners = 10; + + +class EventEmitter : public Nan::ObjectWrap, public EventEmitterStatics { typedef Nan::CopyablePersistentTraits::CopyablePersistent FN_TYPE; typedef std::deque VEC_TYPE; @@ -22,8 +34,51 @@ class EventEmitter : public Nan::ObjectWrap { typedef MAP_TYPE::iterator MAP_IT_TYPE; typedef FNMAP_TYPE::iterator FNMAP_IT_TYPE; + public: + // C++ side emit() method + void emit(const std::string &name, int argc = 0, v8::Local *argv = NULL) { + + VEC_TYPE list = _listeners[name]; + + if (list.empty()) { + return; + } + + for (IT_TYPE it = list.begin(); it != list.end(); ++it) { + + Nan::Callback callback(Nan::New(*it)); + + if ( ! callback.IsEmpty() ) { + callback.Call(argc, argv); + } + + } + + } + + // C++ side on() method + void on(const std::string &name, v8::Local that, const std::string &method) { + + v8::Local code = JS_STR( + "((emitter, name, that, method) => emitter.on(name, that[method]))" + ); + + v8::Local connector = v8::Local::Cast(v8::Script::Compile(code)->Run()); + Nan::Callback connectorCb(connector); + + v8::Local emitter = Nan::New(); + this->Wrap(emitter); + + v8::Local argv[] = { emitter, JS_STR(name.c_str()), that, JS_STR(method.c_str()) }; + connectorCb.Call(4, argv); + + } + + +protected: + EventEmitter () { _maxListeners = _defaultMaxListeners; _freeId = 0; @@ -31,17 +86,7 @@ public: ~EventEmitter () {} - static void extend(v8::Local &ctor, v8::Local &proto) { - - v8::Local ctorFunc = Nan::GetFunction(ctor).ToLocalChecked(); - v8::Local ctorObj = ctorFunc; - - - // -------- static - - Nan::SetMethod(ctorFunc, "listenerCount", jsStaticListenerCount); - ACCESSOR_RW(ctorObj, defaultMaxListeners); - + static void extend(v8::Local &ctor) { // -------- dynamic @@ -60,41 +105,31 @@ public: Nan::SetPrototypeMethod(ctor, "setMaxListeners", jsSetMaxListeners); Nan::SetPrototypeMethod(ctor, "rawListeners", jsRawListeners); + + // -------- static + + v8::Local ctorFunc = Nan::GetFunction(ctor).ToLocalChecked(); + v8::Local ctorObj = ctorFunc; + + Nan::SetMethod(ctorFunc, "listenerCount", jsStaticListenerCount); + ACCESSOR_RW(ctorObj, defaultMaxListeners); + + //Local proto = ctor->PrototypeTemplate(); // ACCESSOR_RW(proto, type); } - void emit(const std::string &name, int argc = 0, v8::Local *argv = NULL) { - - VEC_TYPE &list = _listeners[name]; - - if (list.empty()) { - return; - } - - for (IT_TYPE it = list.begin(); it != list.end(); ++it) { - - Nan::Callback callback(Nan::New(*it)); - - if ( ! callback.IsEmpty() ) { - callback.Call(argc, argv); - } - - } - - } - - - // Deprecated method + // Deprecated static method static NAN_METHOD(jsStaticListenerCount) { - // EventEmitter *emitter = ObjectWrap::Unwrap(info[0]); - // REQ_UTF8_ARG(1, name); + REQ_OBJ_ARG(0, obj); + EventEmitter *emitter = ObjectWrap::Unwrap(obj); + REQ_UTF8_ARG(1, name); - // const VEC_TYPE &list = emitter->_listeners[*name]; + const VEC_TYPE &list = emitter->_listeners[*name]; - // RET_VALUE(JS_NUM(list.size())); + RET_VALUE(JS_INT(static_cast(list.size()))); } @@ -129,7 +164,15 @@ public: REQ_UTF8_ARG(0, name); - emitter->emit(*name, info.Length() - 1, &info[1]); + int length = info.Length(); + + std::vector< v8::Local > args; + + for (int i = 1; i < length; i++) { + args.push_back(info[i]); + } + + emitter->emit(*name, length - 1, &args[0]); } @@ -205,6 +248,9 @@ public: bool isFront ) { THIS_EMITTER; + v8::Local args[] = { info[0], info[1] }; + emitter->emit("newListener", 2, args); + if (isFront) { emitter->_listeners[name].push_front(cb); emitter->_raw[name].push_front(cb); @@ -240,6 +286,9 @@ public: bool isFront ) { THIS_EMITTER; + v8::Local args[] = { info[0], info[1] }; + emitter->emit("newListener", 2, args); + if (isFront) { emitter->_listeners[name].push_front(cb); emitter->_raw[name].push_front(raw); @@ -264,16 +313,16 @@ public: REQ_FUN_ARG(1, raw); v8::Local code = JS_STR( - "((emitter, cb) => (...args) => {\n\ + "((emitter, name, cb) => (...args) => {\n\ cb(...args);\n\ - emitter.removeListener(cb);\n\ + emitter.removeListener(name, cb);\n\ })" ); v8::Local decor = v8::Local::Cast(v8::Script::Compile(code)->Run()); Nan::Callback decorCb(decor); - v8::Local argv[] = { info.This(), raw }; - v8::Local wrap = v8::Local::Cast(decorCb.Call(2, argv)); + v8::Local argv[] = { info.This(), info[0], raw }; + v8::Local wrap = v8::Local::Cast(decorCb.Call(3, argv)); Nan::Persistent persistentWrap; persistentWrap.Reset(wrap); @@ -299,11 +348,27 @@ public: if (info.Length() > 0 && info[0]->IsString()) { + MAP_TYPE tmpMap = emitter->_raw; + emitter->_listeners.clear(); emitter->_raw.clear(); emitter->_wrappedIds.clear(); emitter->_rawIds.clear(); + for (MAP_IT_TYPE itMap = tmpMap.begin(); itMap != tmpMap.end(); ++itMap) { + + const std::string ¤t = itMap->first; + VEC_TYPE &list = itMap->second; + + for (IT_TYPE it = list.begin(); it != list.end(); ++it) { + + v8::Local args[] = { JS_STR(current.c_str()), Nan::New(*it) }; + emitter->emit("removeListener", 2, args); + + } + + } + return; } @@ -326,7 +391,6 @@ public: FN_TYPE fn = *it; for (FNMAP_IT_TYPE itRaw = emitter->_rawIds.begin(); itRaw != emitter->_rawIds.end(); ++itRaw) { - // emitter->_wrappedIds.erase(*it); if (fn == itRaw->second) { removes.push_back(itRaw->first); } @@ -346,9 +410,18 @@ public: } + VEC_TYPE tmpVec = emitter->_raw[name]; + emitter->_listeners[name].clear(); emitter->_raw[name].clear(); + for (IT_TYPE it = tmpVec.begin(); it != tmpVec.end(); ++it) { + + v8::Local args[] = { JS_STR(name.c_str()), Nan::New(*it) }; + emitter->emit("removeListener", 2, args); + + } + } @@ -362,13 +435,14 @@ public: std::string name = std::string(*n); - VEC_TYPE &rawList = emitter->_raw[name]; if (rawList.empty()) { return; } + v8::Local args[] = { info[0], info[1] }; + for (IT_TYPE it = rawList.begin(); it != rawList.end(); ++it) { if (*it == persistentRaw) { @@ -392,6 +466,7 @@ public: } + emitter->emit("removeListener", 2, args); return; } @@ -421,6 +496,8 @@ public: } + emitter->emit("removeListener", 2, args); + } @@ -458,11 +535,6 @@ public: } -private: - - static int _defaultMaxListeners; - - private: int _maxListeners;