From fa989dda665a3d6d8216687833e90ef726c9b5fa Mon Sep 17 00:00:00 2001 From: Luis Blanco Date: Sun, 4 Mar 2018 18:04:25 +0300 Subject: [PATCH] emitter wip --- examples/node-addon/.gitignore | 11 + examples/node-addon/.npmignore | 14 + examples/node-addon/binding.gyp | 88 +++++ examples/node-addon/core.js | 3 + examples/node-addon/cpp/bindings.cpp | 23 ++ examples/node-addon/cpp/common.hpp | 8 + examples/node-addon/cpp/example.cpp | 83 +++++ examples/node-addon/cpp/example.hpp | 37 +++ examples/node-addon/index.js | 10 + examples/node-addon/js/example.js | 7 + examples/node-addon/js/wrapped.js | 12 + examples/node-addon/package.json | 25 ++ examples/node-deps-addon/.gitignore | 9 + examples/node-deps-addon/.npmignore | 11 + examples/node-deps-addon/bin-linux32/.keep | 0 examples/node-deps-addon/bin-linux64/.keep | 0 examples/node-deps-addon/bin-mac64/.keep | 0 examples/node-deps-addon/bin-win32/.keep | 0 examples/node-deps-addon/bin-win64/.keep | 0 examples/node-deps-addon/binding.gyp | 20 ++ examples/node-deps-addon/include/.keep | 0 examples/node-deps-addon/index.js | 3 + examples/node-deps-addon/package.json | 29 ++ include/event-emitter.hpp | 367 ++++++++++++++++++--- 24 files changed, 706 insertions(+), 54 deletions(-) create mode 100644 examples/node-addon/.gitignore create mode 100644 examples/node-addon/.npmignore create mode 100644 examples/node-addon/binding.gyp create mode 100644 examples/node-addon/core.js create mode 100644 examples/node-addon/cpp/bindings.cpp create mode 100644 examples/node-addon/cpp/common.hpp create mode 100644 examples/node-addon/cpp/example.cpp create mode 100644 examples/node-addon/cpp/example.hpp create mode 100644 examples/node-addon/index.js create mode 100644 examples/node-addon/js/example.js create mode 100644 examples/node-addon/js/wrapped.js create mode 100644 examples/node-addon/package.json create mode 100644 examples/node-deps-addon/.gitignore create mode 100644 examples/node-deps-addon/.npmignore create mode 100644 examples/node-deps-addon/bin-linux32/.keep create mode 100644 examples/node-deps-addon/bin-linux64/.keep create mode 100644 examples/node-deps-addon/bin-mac64/.keep create mode 100644 examples/node-deps-addon/bin-win32/.keep create mode 100644 examples/node-deps-addon/bin-win64/.keep create mode 100644 examples/node-deps-addon/binding.gyp create mode 100644 examples/node-deps-addon/include/.keep create mode 100644 examples/node-deps-addon/index.js create mode 100644 examples/node-deps-addon/package.json diff --git a/examples/node-addon/.gitignore b/examples/node-addon/.gitignore new file mode 100644 index 0000000..f4b2eb8 --- /dev/null +++ b/examples/node-addon/.gitignore @@ -0,0 +1,11 @@ +.idea +.cproject +.project +.lock-wscript +build* +.DS_Store +Debug +node_modules +package-lock.json +binary +*.log diff --git a/examples/node-addon/.npmignore b/examples/node-addon/.npmignore new file mode 100644 index 0000000..19d51df --- /dev/null +++ b/examples/node-addon/.npmignore @@ -0,0 +1,14 @@ +.idea +.cproject +.project +.lock-wscript +build* +.DS_Store +Debug +node_modules +package-lock.json +binary +*.log +examples +.gitignore +test diff --git a/examples/node-addon/binding.gyp b/examples/node-addon/binding.gyp new file mode 100644 index 0000000..2bf4651 --- /dev/null +++ b/examples/node-addon/binding.gyp @@ -0,0 +1,88 @@ +{ + 'variables': { + 'rm' : ' + +#include "example.hpp" + +using namespace v8; +using namespace node; +using namespace std; + + +extern "C" { + + +void init(Handle target) { + + Example::init(target); + +} + + +NODE_MODULE(NODE_GYP_MODULE_NAME, init); + + +} // extern "C" diff --git a/examples/node-addon/cpp/common.hpp b/examples/node-addon/cpp/common.hpp new file mode 100644 index 0000000..2ec3285 --- /dev/null +++ b/examples/node-addon/cpp/common.hpp @@ -0,0 +1,8 @@ +#ifndef _COMMON_HPP_ +#define _COMMON_HPP_ + + +#include + + +#endif /* _COMMON_HPP_ */ diff --git a/examples/node-addon/cpp/example.cpp b/examples/node-addon/cpp/example.cpp new file mode 100644 index 0000000..00d2dff --- /dev/null +++ b/examples/node-addon/cpp/example.cpp @@ -0,0 +1,83 @@ +#include +#include + +#include "example.hpp" + +using namespace v8; +using namespace node; +using namespace std; + +#define THIS_EXAMPLE \ + Example *example = ObjectWrap::Unwrap(info.This()); + +#define THIS_CHECK \ + if (body->_isDestroyed) return; + + +Nan::Persistent Example::_constructor; + + +void Example::init(Handle target) { + + Local ctor = Nan::New(newCtor); + + ctor->InstanceTemplate()->SetInternalFieldCount(1); + ctor->SetClassName(JS_STR("Example")); + + + // prototype + Nan::SetPrototypeMethod(ctor, "destroy", destroy); + + 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()); + +} + + +NAN_METHOD(Example::newCtor) { + + CTOR_CHECK("Example"); + + Example *example = new Example(); + example->Wrap(info.This()); + + RET_VALUE(info.This()); + +} + + +Example::Example() { + + _isDestroyed = false; + +} + + +Body::~Body() { + + _destroy(); + +} + + +void Body::_destroy() { DES_CHECK; + + _isDestroyed = true; + + emit("destroy"); + +} + + +NAN_METHOD(Body::destroy) { THIS_BODY; THIS_CHECK; + + example->_destroy(); + +} diff --git a/examples/node-addon/cpp/example.hpp b/examples/node-addon/cpp/example.hpp new file mode 100644 index 0000000..b45b5df --- /dev/null +++ b/examples/node-addon/cpp/example.hpp @@ -0,0 +1,37 @@ +#ifndef _EXAMPLE_HPP_ +#define _EXAMPLE_HPP_ + + +#include + +#include "common.hpp" + + +class Example : public EventEmitter { + +public: + + static void init(v8::Handle target); + + virtual ~Example(); + + +protected: + + Example(); + + static NAN_METHOD(newCtor); + + static NAN_METHOD(destroy); + + +private: + + static Nan::Persistent _constructor; + + bool _isDestroyed; + +}; + + +#endif // _EXAMPLE_HPP_ diff --git a/examples/node-addon/index.js b/examples/node-addon/index.js new file mode 100644 index 0000000..dfff914 --- /dev/null +++ b/examples/node-addon/index.js @@ -0,0 +1,10 @@ +'use strict'; + +const Wrapped = require('./js/wrapped'); +const Example = require('./js/example'); + +console.log('Example', Example); + +console.log('Wrapped', Wrapped); + +module.exports = { Wrapped, Example }; diff --git a/examples/node-addon/js/example.js b/examples/node-addon/js/example.js new file mode 100644 index 0000000..d3f20bd --- /dev/null +++ b/examples/node-addon/js/example.js @@ -0,0 +1,7 @@ +'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 new file mode 100644 index 0000000..b28abb9 --- /dev/null +++ b/examples/node-addon/js/wrapped.js @@ -0,0 +1,12 @@ +'use strict'; + + +const { Example } = require('../core'); + + +class Wrapped extends Example { + +} + + +module.exports = Wrapped; diff --git a/examples/node-addon/package.json b/examples/node-addon/package.json new file mode 100644 index 0000000..41b061e --- /dev/null +++ b/examples/node-addon/package.json @@ -0,0 +1,25 @@ +{ + "name": "example", + "version": "0.0.1", + "description": "EXAMPLE", + "main": "index.js", + "author": "Luis Blanco ", + "keywords": [ + "addon" + ], + "maintainers": [ + { + "name": "Luis Blanco", + "email": "raubtierxxx@gmail.com", + "skype": "rauber666" + } + ], + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/raub/node-addon-tools" + }, + "dependencies": { + "addon-tools-raub": "../../" + } +} diff --git a/examples/node-deps-addon/.gitignore b/examples/node-deps-addon/.gitignore new file mode 100644 index 0000000..744f3bd --- /dev/null +++ b/examples/node-deps-addon/.gitignore @@ -0,0 +1,9 @@ +node_modules +*.log +build* +.DS_Store +*.pro.user +*.exp +*.pdb +*.ilk +package-lock.json diff --git a/examples/node-deps-addon/.npmignore b/examples/node-deps-addon/.npmignore new file mode 100644 index 0000000..d8b5772 --- /dev/null +++ b/examples/node-deps-addon/.npmignore @@ -0,0 +1,11 @@ +node_modules +*.log +build* +.DS_Store +*.pro.user +*.exp +*.pdb +*.ilk +.gitignore +package-lock.json +test diff --git a/examples/node-deps-addon/bin-linux32/.keep b/examples/node-deps-addon/bin-linux32/.keep new file mode 100644 index 0000000..e69de29 diff --git a/examples/node-deps-addon/bin-linux64/.keep b/examples/node-deps-addon/bin-linux64/.keep new file mode 100644 index 0000000..e69de29 diff --git a/examples/node-deps-addon/bin-mac64/.keep b/examples/node-deps-addon/bin-mac64/.keep new file mode 100644 index 0000000..e69de29 diff --git a/examples/node-deps-addon/bin-win32/.keep b/examples/node-deps-addon/bin-win32/.keep new file mode 100644 index 0000000..e69de29 diff --git a/examples/node-deps-addon/bin-win64/.keep b/examples/node-deps-addon/bin-win64/.keep new file mode 100644 index 0000000..e69de29 diff --git a/examples/node-deps-addon/binding.gyp b/examples/node-deps-addon/binding.gyp new file mode 100644 index 0000000..9847bcd --- /dev/null +++ b/examples/node-deps-addon/binding.gyp @@ -0,0 +1,20 @@ +{ + 'variables': { + 'rm' : '", + "description": "EXAMPLE", + "version": "0.0.1", + "main": "index.js", + "keywords": [ + "deps", + "dependency", + "lib", + "library", + "binary" + ], + "maintainers": [ + { + "name": "Luis Blanco", + "email": "raubtierxxx@gmail.com", + "skype": "rauber666" + } + ], + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/raub/node-addon-tools" + }, + "dependencies": { + "addon-tools-raub": "../../" + } +} diff --git a/include/event-emitter.hpp b/include/event-emitter.hpp index a4dbb39..fd22904 100644 --- a/include/event-emitter.hpp +++ b/include/event-emitter.hpp @@ -5,41 +5,47 @@ #include #include -#include +#include -#define THIS_VIEW \ +#define THIS_EMITTER \ EventEmitter *emitter = ObjectWrap::Unwrap(info.This()); class EventEmitter : public Nan::ObjectWrap { typedef Nan::CopyablePersistentTraits::CopyablePersistent FN_TYPE; - typedef std::vector VEC_TYPE; + typedef std::deque VEC_TYPE; typedef std::map MAP_TYPE; + typedef std::map FNMAP_TYPE; typedef VEC_TYPE::iterator IT_TYPE; + typedef MAP_TYPE::iterator MAP_IT_TYPE; public: - Emitter () {} - ~Emitter () {} + EventEmitter () { + _maxListeners = _defaultMaxListeners; + } + ~EventEmitter () {} - init(v8::Local &ctor, v8::Local &proto) { + 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(ctorFunc, defaultMaxListeners); + ACCESSOR_RW(ctorObj, defaultMaxListeners); // -------- dynamic Nan::SetPrototypeMethod(ctor, "listenerCount", jsListenerCount); Nan::SetPrototypeMethod(ctor, "addListener", jsAddListener); + Nan::SetPrototypeMethod(ctor, "emit", jsEmit); Nan::SetPrototypeMethod(ctor, "eventNames", jsEventNames); Nan::SetPrototypeMethod(ctor, "getMaxListeners", jsGetMaxListeners); Nan::SetPrototypeMethod(ctor, "listeners", jsListeners); @@ -57,9 +63,9 @@ public: } - void emit(const std::string &name, int argc, Local argv[]) { + void emit(const std::string &name, int argc = 0, v8::Local *argv = NULL) { - const VEC_TYPE &list = _listeners[name]; + VEC_TYPE &list = _listeners[name]; if (list.empty()) { return; @@ -78,59 +84,83 @@ public: } - - - - NAN_METHOD(jsStaticListenerCount) { + // Deprecated method + static NAN_METHOD(jsStaticListenerCount) { - Emitter *emitter = ObjectWrap::Unwrap(info[0]); + EventEmitter *emitter = ObjectWrap::Unwrap(info[0]); REQ_UTF8_ARG(1, name); const VEC_TYPE &list = emitter->_listeners[*name]; - Local jsListeners = Nan::New(list.size()); - - if (list.empty()) { - RET_VALUE(jsListeners); - return; - } - - for (IT_TYPE it = list.begin(), int i = 0; it != list.end(); ++it) { - - jsListeners->Set(JS_INT(j), Nan::New(*it)); - } - - RET_VALUE(jsListeners); + RET_VALUE(JS_INT(list.size())); } + static NAN_SETTER(defaultMaxListenersSetter) { THIS_EMITTER; SETTER_INT32_ARG; + + EventEmitter::_defaultMaxListeners = v; + + } - NAN_METHOD(jsStaticListenerCount) { + static NAN_GETTER(defaultMaxListenersGetter) { THIS_EMITTER; - Emitter *emitter = ObjectWrap::Unwrap(info[0]); - REQ_UTF8_ARG(1, name); - - const VEC_TYPE &list = emitter->_listeners[*name]; - - Local jsListeners = Nan::New(list.size()); - - if (list.empty()) { - RET_VALUE(jsListeners); - return; - } - - for (IT_TYPE it = list.begin(), int i = 0; it != list.end(); ++it) { - - jsListeners->Set(JS_INT(j), Nan::New(*it)); - } - - RET_VALUE(jsListeners); + RET_VALUE(JS_INT(EventEmitter::_defaultMaxListeners)); } - NAN_METHOD(jsListenerCount) { THIS_EMITTER; + static NAN_METHOD(jsAddListener) { THIS_EMITTER; + + REQ_UTF8_ARG(0, name); + REQ_FUN_ARG(1, cb); + + Nan::Persistent persistentCb; + persistentCb.Reset(cb); + + emitter->_listeners[std::string(*name)].push_back(persistentCb); + + } + + + static NAN_METHOD(jsEmit) { THIS_EMITTER; + + REQ_UTF8_ARG(0, name); + + emitter->emit(*name, info.Length() - 1, &info[1]); + + } + + + static NAN_METHOD(jsEventNames) { THIS_EMITTER; + + v8::Local jsNames = Nan::New(emitter->_listeners.size()); + + if (emitter->_listeners.empty()) { + RET_VALUE(jsNames); + return; + } + + int i = 0; + for (MAP_IT_TYPE it = emitter->_listeners.begin(); it != emitter->_listeners.end(); ++it, i++) { + + jsNames->Set(JS_INT(i), JS_STR(it->first)); + + } + + RET_VALUE(jsNames); + + } + + + static NAN_METHOD(jsGetMaxListeners) { THIS_EMITTER; + + RET_VALUE(JS_INT(emitter->_maxListeners)); + + } + + + static NAN_METHOD(jsListenerCount) { THIS_EMITTER; REQ_UTF8_ARG(0, name); @@ -141,7 +171,53 @@ public: } - NAN_METHOD(jsAddListener) { THIS_EMITTER; + static NAN_METHOD(jsListeners) { THIS_EMITTER; + + REQ_UTF8_ARG(0, name); + + VEC_TYPE &list = emitter->_listeners[*name]; + + v8::Local jsListeners = Nan::New(list.size()); + + if (list.empty()) { + RET_VALUE(jsListeners); + return; + } + + int i = 0; + for (IT_TYPE it = list.begin(); it != list.end(); ++it, i++) { + + jsListeners->Set(JS_INT(i), Nan::New(*it)); + + } + + RET_VALUE(jsListeners); + + } + + + static inline void _addListener( + const Nan::FunctionCallbackInfo &info, + const std::string &name, + Nan::Persistent &cb, + bool isFront + ) { THIS_EMITTER; + + if (isFront) { + emitter->_listeners[name].push_front(cb); + emitter->_raw[name].push_front(cb); + } else { + emitter->_listeners[name].push_back(cb); + emitter->_raw[name].push_back(cb); + } + + } + + + static inline void _wrapListener( + const Nan::FunctionCallbackInfo &info, + bool isFront = false + ) { REQ_UTF8_ARG(0, name); REQ_FUN_ARG(1, cb); @@ -149,28 +225,211 @@ public: Nan::Persistent persistentCb; persistentCb.Reset(cb); - emitter->_listeners[std::string(*name)].push_back(persistentCb); + _addListener(info, *name, persistentCb, isFront); } - NAN_METHOD(jsOn) { THIS_EMITTER; + static inline void _addOnceListener( + const Nan::FunctionCallbackInfo &info, + const std::string &name, + Nan::Persistent &raw, + Nan::Persistent &cb, + bool isFront + ) { THIS_EMITTER; + + if (isFront) { + emitter->_listeners[name].push_front(cb); + emitter->_raw[name].push_front(raw); + } else { + emitter->_listeners[name].push_back(cb); + emitter->_raw[name].push_back(raw); + } + emitter->_wrapped[raw] = cb; + + } + + + static inline void _wrapOnceListener( + const Nan::FunctionCallbackInfo &info, + bool isFront = false + ) { REQ_UTF8_ARG(0, name); - REQ_FUN_ARG(1, cb); + REQ_FUN_ARG(1, raw); - Nan::Persistent persistentCb; - persistentCb.Reset(cb); + v8::Local code = JS_STR( + "((emitter, cb) => (...args) => {\n\ + cb(...args);\n\ + emitter.removeListener(cb);\n\ + })" + ); - emitter->_listeners[std::string(*name)].push_back(persistentCb); + 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)); + + Nan::Persistent persistentWrap; + persistentWrap.Reset(wrap); + + Nan::Persistent persistentRaw; + persistentRaw.Reset(raw); + + _addOnceListener(info, *name, persistentRaw, persistentWrap, isFront); } + static NAN_METHOD(jsOn) { _wrapListener(info); } + + static NAN_METHOD(jsOnce) { _wrapOnceListener(info); } + + static NAN_METHOD(jsPrependListener) { _wrapListener(info, true); } + + static NAN_METHOD(jsPrependOnceListener) { _wrapOnceListener(info, true); } + + + static NAN_METHOD(jsRemoveAllListeners) { THIS_EMITTER; + + if (info->Length > 0 && info[0]->IsString()) { + + _listeners->clear(); + _raw->clear(); + _wrapped->clear(); + + return; + + } + + REQ_UTF8_ARG(0, n); + + std::string name = std::string(*n); + VEC_TYPE &list = emitter->_raw[name]; + + if (list.empty()) { + return; + } + + for (IT_TYPE it = list.begin(); it != list.end(); ++it) { + + emitter->_wrapped.erase(*it) + + } + + emitter->_listeners[*name].clear(); + emitter->_raw[*name].clear(); + + } + + + static NAN_METHOD(jsRemoveListener) { THIS_EMITTER; + + REQ_UTF8_ARG(0, n); + REQ_FUN_ARG(1, raw); + + Nan::Persistent persistentCb; + persistentCb.Reset(raw); + + std::string name = std::string(*n); + + + VEC_TYPE &rawList = emitter->_raw[name]; + + if (rawList.empty()) { + return; + } + + for (IT_TYPE it = rawList.begin(); it != rawList.end(); ++it) { + + if (*it === persistentCb) { + rawList.erase(it); + break; + } + + } + + + VEC_TYPE &wrapList = emitter->_listeners[name]; + + if (emitter->_wrapped.count(persistentCb) == 0) { + + for (IT_TYPE it = wrapList.begin(); it != wrapList.end(); ++it) { + + if (*it === persistentCb) { + wrapList.erase(it); + break; + } + + } + + return; + + } + + + const FN_TYPE &wrapped = _wrapped[persistentCb]; + + for (IT_TYPE it = wrapList.begin(); it != wrapList.end(); ++it) { + + if (*it === wrapped) { + wrapList.erase(it); + break; + } + + } + + emitter->_wrapped.erase(persistentCb); + + } + + + static NAN_METHOD(jsSetMaxListeners) { THIS_EMITTER; + + REQ_INT32_ARG(0, value); + + emitter->_maxListeners = value; + + } + + + static NAN_METHOD(jsRawListeners) { THIS_EMITTER; + + REQ_UTF8_ARG(0, name); + + VEC_TYPE &list = emitter->_raw[*name]; + + Local jsListeners = Nan::New(list.size()); + + if (list.empty()) { + RET_VALUE(jsListeners); + return; + } + + int i = 0; + for (IT_TYPE it = list.begin(); it != list.end(); ++it, i++) { + + jsListeners->Set(JS_INT(i), Nan::New(*it)); + + } + + RET_VALUE(jsListeners); + + } + private: + static int _defaultMaxListeners; + + +private: + + int _maxListeners; + MAP_TYPE _listeners; + MAP_TYPE _raw; + FNMAP_TYPE _wrapped; };