From be1f79e4fc4766d82ee5434b06997529318a1f05 Mon Sep 17 00:00:00 2001 From: raub Date: Wed, 7 Mar 2018 17:47:58 +0300 Subject: [PATCH] additional fixes and readme on eventemitter --- README.md | 69 +++++++++++++++++++- examples/node-addon/cpp/example.cpp | 26 ++++---- examples/node-addon/cpp/example.hpp | 6 -- examples/node-addon/index.js | 3 + include/event-emitter.hpp | 97 +++++++++++++---------------- 5 files changed, 129 insertions(+), 72 deletions(-) diff --git a/README.md b/README.md index 219ebc1..fa795a3 100644 --- a/README.md +++ b/README.md @@ -568,14 +568,81 @@ For Windows the `/y` flag was embedded. A C++ implementation of [Events API](https://nodejs.org/api/events.html). +NOTE: This implementation has some minor deviations from the above standard. +Specifically there is no static `EventEmitter.defaultMaxListeners` property. +However the dynamic one persists. + An example of it's usage can be found in **examples/node-addon** directory. There is `Example` class, implemented in **cpp/example.cpp**, that inherits EventEmitter behavior and is exported to JS. -For C++ side `EventEmitter` has following public methods: +For the C++ side `EventEmitter` has following public methods: + * `void emit(const std::string &name, int argc = 0, v8::Local *argv = NULL)` emits an event with the given `name` and, optionally, some additional arguments where `argc` is the number of arguments and `argv` is a pointer to the arguments array. + * `void on(const std::string &name, v8::Local that, const std::string &method)` subscribes `that[method]` to receive `name` events from this emitter, basically `emitter.on(name, that[method])`. + + +Be sure to add the include directory in **binding.gyp**: + +``` + 'include_dirs': [ + ' + +class Example : public EventEmitter { + ... +} +``` + + +First add EventEmitter dynamic methods to the prototype via +`static void extendPrototype(v8::Local &proto)` and then, +after constructor function instance is created, +`static void extendConstructor(v8::Local &ctorFn)`: + +``` +void Example::init(Handle target) { + + Local proto = Nan::New(newCtor); + + proto->InstanceTemplate()->SetInternalFieldCount(1); + proto->SetClassName(JS_STR("Example")); + + + // -------- dynamic + + // Add EventEmitter methods + extendPrototype(proto); + + Nan::SetPrototypeMethod(proto, "destroy", destroy); + + + // -------- static + + Local ctor = Nan::GetFunction(proto).ToLocalChecked(); + + extendConstructor(ctor); + + + _constructor.Reset(ctor); + Nan::Set(target, JS_STR("Example"), ctor); + +} +``` + +NOTE: after a `v8::Function` is created from the `v8::FunctionTemplate`, no +additional methods can be added to the prototype. Also static members can only +be added to the created `v8::Function` representing the constructor. This is why +there are 2 extend methods: `extendPrototype` and `extendConstructor`. diff --git a/examples/node-addon/cpp/example.cpp b/examples/node-addon/cpp/example.cpp index 0fafe5b..aa2834f 100644 --- a/examples/node-addon/cpp/example.cpp +++ b/examples/node-addon/cpp/example.cpp @@ -19,25 +19,29 @@ Nan::Persistent Example::_constructor; void Example::init(Handle target) { - Local ctor = Nan::New(newCtor); + Local proto = Nan::New(newCtor); - ctor->InstanceTemplate()->SetInternalFieldCount(1); - ctor->SetClassName(JS_STR("Example")); + proto->InstanceTemplate()->SetInternalFieldCount(1); + proto->SetClassName(JS_STR("Example")); - // prototype - Nan::SetPrototypeMethod(ctor, "destroy", destroy); + // -------- dynamic - extend(ctor); + // Add EventEmitter methods + extendPrototype(proto); + + Nan::SetPrototypeMethod(proto, "destroy", destroy); - Local proto = ctor->PrototypeTemplate(); - // ACCESSOR_RW(proto, prop); + // -------- static + + Local ctor = Nan::GetFunction(proto).ToLocalChecked(); + + extendConstructor(ctor); - - _constructor.Reset(Nan::GetFunction(ctor).ToLocalChecked()); - Nan::Set(target, JS_STR("Example"), Nan::GetFunction(ctor).ToLocalChecked()); + _constructor.Reset(ctor); + Nan::Set(target, JS_STR("Example"), ctor); } diff --git a/examples/node-addon/cpp/example.hpp b/examples/node-addon/cpp/example.hpp index 81e7c48..c2bb23a 100644 --- a/examples/node-addon/cpp/example.hpp +++ b/examples/node-addon/cpp/example.hpp @@ -25,12 +25,6 @@ protected: static NAN_METHOD(destroy); -protected: - - void setDefaultMaxListeners(int count) { } - int getDefaultMaxListeners() { return 10; } - - private: void _destroy(); diff --git a/examples/node-addon/index.js b/examples/node-addon/index.js index b5ea524..9aedc36 100644 --- a/examples/node-addon/index.js +++ b/examples/node-addon/index.js @@ -52,4 +52,7 @@ example.emit('evt2', 112, '222'); console.log('example 1', example); +Example.defaultMaxListeners = -1; + + module.exports = Example; diff --git a/include/event-emitter.hpp b/include/event-emitter.hpp index dcfe1eb..1302fe3 100644 --- a/include/event-emitter.hpp +++ b/include/event-emitter.hpp @@ -6,25 +6,14 @@ #include #include -// #include -> std::cout << "..." << std::endl; +#include // -> std::cout << "..." << std::endl; #define THIS_EMITTER \ EventEmitter *emitter = ObjectWrap::Unwrap(info.This()); -// Use the dummy template class 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 { +class EventEmitter : public Nan::ObjectWrap { typedef Nan::CopyablePersistentTraits::CopyablePersistent FN_TYPE; typedef std::deque VEC_TYPE; @@ -38,42 +27,42 @@ class EventEmitter : public Nan::ObjectWrap, public EventEmitterStatics { protected: EventEmitter () { - _maxListeners = _defaultMaxListeners; + _maxListeners = 0; _freeId = 0; } - ~EventEmitter () {} + + virtual ~EventEmitter () {} - static void extend(v8::Local &ctor) { + static void extendPrototype(v8::Local &proto) { - // -------- 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); - Nan::SetPrototypeMethod(ctor, "on", jsOn); - Nan::SetPrototypeMethod(ctor, "once", jsOnce); - Nan::SetPrototypeMethod(ctor, "prependListener", jsPrependListener); - Nan::SetPrototypeMethod(ctor, "prependOnceListener", jsPrependOnceListener); - Nan::SetPrototypeMethod(ctor, "removeAllListeners", jsRemoveAllListeners); - Nan::SetPrototypeMethod(ctor, "removeListener", jsRemoveListener); - 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); + Nan::SetPrototypeMethod(proto, "listenerCount", jsListenerCount); + Nan::SetPrototypeMethod(proto, "addListener", jsAddListener); + Nan::SetPrototypeMethod(proto, "emit", jsEmit); + Nan::SetPrototypeMethod(proto, "eventNames", jsEventNames); + Nan::SetPrototypeMethod(proto, "getMaxListeners", jsGetMaxListeners); + Nan::SetPrototypeMethod(proto, "listeners", jsListeners); + Nan::SetPrototypeMethod(proto, "on", jsOn); + Nan::SetPrototypeMethod(proto, "once", jsOnce); + Nan::SetPrototypeMethod(proto, "prependListener", jsPrependListener); + Nan::SetPrototypeMethod(proto, "prependOnceListener", jsPrependOnceListener); + Nan::SetPrototypeMethod(proto, "removeAllListeners", jsRemoveAllListeners); + Nan::SetPrototypeMethod(proto, "removeListener", jsRemoveListener); + Nan::SetPrototypeMethod(proto, "setMaxListeners", jsSetMaxListeners); + Nan::SetPrototypeMethod(proto, "rawListeners", jsRawListeners); } + + static void extendConstructor(v8::Local &ctorFn) { + + v8::Local ctor = v8::Local::Cast(ctorFn); + + Nan::SetMethod(ctor, "listenerCount", jsStaticListenerCount); + + } + + public: // C++ side emit() method @@ -139,19 +128,6 @@ protected: } - static NAN_SETTER(defaultMaxListenersSetter) { THIS_EMITTER; SETTER_INT32_ARG; - - _defaultMaxListeners = v; - - } - - static NAN_GETTER(defaultMaxListenersGetter) { THIS_EMITTER; - - RET_VALUE(JS_INT(_defaultMaxListeners)); - - } - - static NAN_METHOD(jsAddListener) { _wrapListener(info); } @@ -254,6 +230,19 @@ protected: emitter->_raw[name].push_back(cb); } + if (emitter->_maxListeners > 0 && emitter->_raw.size() > emitter->_maxListeners) { + + std::cout << "EventEmitter Warning: too many listeners ("; + std::cout << emitter->_raw.size() << " > " << emitter->_maxListeners; + std::cout << ") possible memory leak." << std::endl; + + v8::Local code = JS_STR("(new Error()).stack"); + v8::Local stack = v8::Local::Cast(v8::Script::Compile(code)->Run()); + Nan::Utf8String stackStr(stack); + std::cout << *stackStr << std::endl; + + } + }