additional fixes and readme on eventemitter

This commit is contained in:
raub 2018-03-07 17:47:58 +03:00
parent 0f9ada3680
commit be1f79e4fc
5 changed files with 129 additions and 72 deletions

View File

@ -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<v8::Value> *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<v8::Value> 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': [
'<!@(node -e "require(\'addon-tools-raub\').include()")',
],
```
Include the **event-emitter.hpp**, it already includes **addon-tools.hpp**.
Inherit from `EventEmitter`, it already inherits from `Nan::ObjectWrap`:
```
#include <event-emitter.hpp>
class Example : public EventEmitter {
...
}
```
First add EventEmitter dynamic methods to the prototype via
`static void extendPrototype(v8::Local<v8::FunctionTemplate> &proto)` and then,
after constructor function instance is created,
`static void extendConstructor(v8::Local<v8::Function> &ctorFn)`:
```
void Example::init(Handle<Object> target) {
Local<FunctionTemplate> proto = Nan::New<FunctionTemplate>(newCtor);
proto->InstanceTemplate()->SetInternalFieldCount(1);
proto->SetClassName(JS_STR("Example"));
// -------- dynamic
// Add EventEmitter methods
extendPrototype(proto);
Nan::SetPrototypeMethod(proto, "destroy", destroy);
// -------- static
Local<Function> 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`.

View File

@ -19,25 +19,29 @@ Nan::Persistent<v8::Function> Example::_constructor;
void Example::init(Handle<Object> target) {
Local<FunctionTemplate> ctor = Nan::New<FunctionTemplate>(newCtor);
Local<FunctionTemplate> proto = Nan::New<FunctionTemplate>(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<ObjectTemplate> proto = ctor->PrototypeTemplate();
// ACCESSOR_RW(proto, prop);
// -------- static
Local<Function> 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);
}

View File

@ -25,12 +25,6 @@ protected:
static NAN_METHOD(destroy);
protected:
void setDefaultMaxListeners(int count) { }
int getDefaultMaxListeners() { return 10; }
private:
void _destroy();

View File

@ -52,4 +52,7 @@ example.emit('evt2', 112, '222');
console.log('example 1', example);
Example.defaultMaxListeners = -1;
module.exports = Example;

View File

@ -6,25 +6,14 @@
#include <map>
#include <deque>
// #include <iostream> -> std::cout << "..." << std::endl;
#include <iostream> // -> std::cout << "..." << std::endl;
#define THIS_EMITTER \
EventEmitter *emitter = ObjectWrap::Unwrap<EventEmitter>(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<Dummy>::_defaultMaxListeners = 10;
class EventEmitter : public Nan::ObjectWrap, public EventEmitterStatics<void> {
class EventEmitter : public Nan::ObjectWrap {
typedef Nan::CopyablePersistentTraits<v8::Function>::CopyablePersistent FN_TYPE;
typedef std::deque<FN_TYPE> VEC_TYPE;
@ -38,42 +27,42 @@ class EventEmitter : public Nan::ObjectWrap, public EventEmitterStatics<void> {
protected:
EventEmitter () {
_maxListeners = _defaultMaxListeners;
_maxListeners = 0;
_freeId = 0;
}
~EventEmitter () {}
virtual ~EventEmitter () {}
static void extend(v8::Local<v8::FunctionTemplate> &ctor) {
static void extendPrototype(v8::Local<v8::FunctionTemplate> &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<v8::Function> ctorFunc = Nan::GetFunction(ctor).ToLocalChecked();
v8::Local<v8::Object> 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<v8::Function> &ctorFn) {
v8::Local<v8::Object> ctor = v8::Local<v8::Object>::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<v8::String> code = JS_STR("(new Error()).stack");
v8::Local<v8::String> stack = v8::Local<v8::String>::Cast(v8::Script::Compile(code)->Run());
Nan::Utf8String stackStr(stack);
std::cout << *stackStr << std::endl;
}
}