additional fixes and readme on eventemitter
This commit is contained in:
parent
0f9ada3680
commit
be1f79e4fc
69
README.md
69
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).
|
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.
|
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
|
There is `Example` class, implemented in **cpp/example.cpp**, that inherits
|
||||||
EventEmitter behavior and is exported to JS.
|
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)`
|
* `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
|
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.
|
`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)`
|
* `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
|
subscribes `that[method]` to receive `name` events from this emitter, basically
|
||||||
`emitter.on(name, that[method])`.
|
`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`.
|
||||||
|
|
|
@ -19,25 +19,29 @@ Nan::Persistent<v8::Function> Example::_constructor;
|
||||||
|
|
||||||
void Example::init(Handle<Object> target) {
|
void Example::init(Handle<Object> target) {
|
||||||
|
|
||||||
Local<FunctionTemplate> ctor = Nan::New<FunctionTemplate>(newCtor);
|
Local<FunctionTemplate> proto = Nan::New<FunctionTemplate>(newCtor);
|
||||||
|
|
||||||
ctor->InstanceTemplate()->SetInternalFieldCount(1);
|
proto->InstanceTemplate()->SetInternalFieldCount(1);
|
||||||
ctor->SetClassName(JS_STR("Example"));
|
proto->SetClassName(JS_STR("Example"));
|
||||||
|
|
||||||
|
|
||||||
// prototype
|
// -------- dynamic
|
||||||
Nan::SetPrototypeMethod(ctor, "destroy", destroy);
|
|
||||||
|
|
||||||
extend(ctor);
|
// Add EventEmitter methods
|
||||||
|
extendPrototype(proto);
|
||||||
|
|
||||||
|
Nan::SetPrototypeMethod(proto, "destroy", destroy);
|
||||||
|
|
||||||
|
|
||||||
Local<ObjectTemplate> proto = ctor->PrototypeTemplate();
|
// -------- static
|
||||||
// ACCESSOR_RW(proto, prop);
|
|
||||||
|
Local<Function> ctor = Nan::GetFunction(proto).ToLocalChecked();
|
||||||
|
|
||||||
|
extendConstructor(ctor);
|
||||||
|
|
||||||
|
|
||||||
|
_constructor.Reset(ctor);
|
||||||
_constructor.Reset(Nan::GetFunction(ctor).ToLocalChecked());
|
Nan::Set(target, JS_STR("Example"), ctor);
|
||||||
Nan::Set(target, JS_STR("Example"), Nan::GetFunction(ctor).ToLocalChecked());
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,12 +25,6 @@ protected:
|
||||||
static NAN_METHOD(destroy);
|
static NAN_METHOD(destroy);
|
||||||
|
|
||||||
|
|
||||||
protected:
|
|
||||||
|
|
||||||
void setDefaultMaxListeners(int count) { }
|
|
||||||
int getDefaultMaxListeners() { return 10; }
|
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
void _destroy();
|
void _destroy();
|
||||||
|
|
|
@ -52,4 +52,7 @@ example.emit('evt2', 112, '222');
|
||||||
console.log('example 1', example);
|
console.log('example 1', example);
|
||||||
|
|
||||||
|
|
||||||
|
Example.defaultMaxListeners = -1;
|
||||||
|
|
||||||
|
|
||||||
module.exports = Example;
|
module.exports = Example;
|
||||||
|
|
|
@ -6,25 +6,14 @@
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <deque>
|
#include <deque>
|
||||||
// #include <iostream> -> std::cout << "..." << std::endl;
|
#include <iostream> // -> std::cout << "..." << std::endl;
|
||||||
|
|
||||||
|
|
||||||
#define THIS_EMITTER \
|
#define THIS_EMITTER \
|
||||||
EventEmitter *emitter = ObjectWrap::Unwrap<EventEmitter>(info.This());
|
EventEmitter *emitter = ObjectWrap::Unwrap<EventEmitter>(info.This());
|
||||||
|
|
||||||
|
|
||||||
// Use the dummy template class for static-int initialization
|
class EventEmitter : public Nan::ObjectWrap {
|
||||||
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> {
|
|
||||||
|
|
||||||
typedef Nan::CopyablePersistentTraits<v8::Function>::CopyablePersistent FN_TYPE;
|
typedef Nan::CopyablePersistentTraits<v8::Function>::CopyablePersistent FN_TYPE;
|
||||||
typedef std::deque<FN_TYPE> VEC_TYPE;
|
typedef std::deque<FN_TYPE> VEC_TYPE;
|
||||||
|
@ -38,42 +27,42 @@ class EventEmitter : public Nan::ObjectWrap, public EventEmitterStatics<void> {
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
EventEmitter () {
|
EventEmitter () {
|
||||||
_maxListeners = _defaultMaxListeners;
|
_maxListeners = 0;
|
||||||
_freeId = 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(proto, "listenerCount", jsListenerCount);
|
||||||
|
Nan::SetPrototypeMethod(proto, "addListener", jsAddListener);
|
||||||
Nan::SetPrototypeMethod(ctor, "listenerCount", jsListenerCount);
|
Nan::SetPrototypeMethod(proto, "emit", jsEmit);
|
||||||
Nan::SetPrototypeMethod(ctor, "addListener", jsAddListener);
|
Nan::SetPrototypeMethod(proto, "eventNames", jsEventNames);
|
||||||
Nan::SetPrototypeMethod(ctor, "emit", jsEmit);
|
Nan::SetPrototypeMethod(proto, "getMaxListeners", jsGetMaxListeners);
|
||||||
Nan::SetPrototypeMethod(ctor, "eventNames", jsEventNames);
|
Nan::SetPrototypeMethod(proto, "listeners", jsListeners);
|
||||||
Nan::SetPrototypeMethod(ctor, "getMaxListeners", jsGetMaxListeners);
|
Nan::SetPrototypeMethod(proto, "on", jsOn);
|
||||||
Nan::SetPrototypeMethod(ctor, "listeners", jsListeners);
|
Nan::SetPrototypeMethod(proto, "once", jsOnce);
|
||||||
Nan::SetPrototypeMethod(ctor, "on", jsOn);
|
Nan::SetPrototypeMethod(proto, "prependListener", jsPrependListener);
|
||||||
Nan::SetPrototypeMethod(ctor, "once", jsOnce);
|
Nan::SetPrototypeMethod(proto, "prependOnceListener", jsPrependOnceListener);
|
||||||
Nan::SetPrototypeMethod(ctor, "prependListener", jsPrependListener);
|
Nan::SetPrototypeMethod(proto, "removeAllListeners", jsRemoveAllListeners);
|
||||||
Nan::SetPrototypeMethod(ctor, "prependOnceListener", jsPrependOnceListener);
|
Nan::SetPrototypeMethod(proto, "removeListener", jsRemoveListener);
|
||||||
Nan::SetPrototypeMethod(ctor, "removeAllListeners", jsRemoveAllListeners);
|
Nan::SetPrototypeMethod(proto, "setMaxListeners", jsSetMaxListeners);
|
||||||
Nan::SetPrototypeMethod(ctor, "removeListener", jsRemoveListener);
|
Nan::SetPrototypeMethod(proto, "rawListeners", jsRawListeners);
|
||||||
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);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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:
|
public:
|
||||||
|
|
||||||
// C++ side emit() method
|
// 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); }
|
static NAN_METHOD(jsAddListener) { _wrapListener(info); }
|
||||||
|
|
||||||
|
|
||||||
|
@ -254,6 +230,19 @@ protected:
|
||||||
emitter->_raw[name].push_back(cb);
|
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;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue