wip new arch

This commit is contained in:
Luis Blanco 2019-08-05 00:47:25 +03:00
parent 1fb87351a5
commit 95173cd89c
35 changed files with 434 additions and 1647 deletions

7
.gitignore vendored
View File

@ -1,11 +1,8 @@
.idea
.cproject
.project
.idea
.lock-wscript
build*/
.DS_Store
Debug/
.project
node_modules/
package-lock.json
binary/
*.log

View File

@ -1,15 +1,12 @@
*.log
.cproject
.eslintrc
.gitignore
.idea
.lock-wscript
.DS_Store
.project
binary/
build*/
CPPLINT.cfg
Debug/
examples/
node_modules/
package-lock.json
.gitignore
CPPLINT.cfg
.eslintrc
test/
qt/
*.log

View File

@ -1,7 +1,7 @@
language: node_js
node_js:
- "10.13.0"
- "10.16.1"
matrix:

110
README.md
View File

@ -14,14 +14,15 @@ This is a part of [Node3D](https://github.com/node-3d) project.
Helpers for Node.js addons and dependency packages:
* `consoleLog()` C++ implementation.
* `EventEmitter` C++ implementation.
* C++ macros and shortcuts.
* `consoleLog()` C++ helper.
* `eventEmit()` C++ helper.
* `getData()` C++ helper.
* Crossplatform commands for GYP: `cp`, `rm`, `mkdir`.
* Regarded platforms: win x32/x64, linux x64, mac x64.
* Supported platforms (x64): Windows, Linux, OSX.
Useful links: [V8 Ref](https://v8.paulfryzel.com/docs/master/),
[Nan Docs](https://github.com/nodejs/nan#api),
Useful links: [N-API Docs](https://nodejs.org/api/n-api.html),
[Napi Docs](https://github.com/nodejs/node-addon-api/blob/master/doc/setup.md),
[GYP Docs](https://gyp.gsrc.io/docs/UserDocumentation.md).
---
@ -37,10 +38,10 @@ Useful links: [V8 Ref](https://v8.paulfryzel.com/docs/master/),
[Crossplatform commands](#crossplatform-commands)
[Class EventEmitter](#class-eventemitter)
[Function consoleLog](#function-consolelog)
[Function eventEmit](#function-eventEmit)
---
@ -49,85 +50,39 @@ Useful links: [V8 Ref](https://v8.paulfryzel.com/docs/master/),
### binding.gyp
<details>
### Crossplatform commands
<summary>Crossplatform commands</summary>
```
'variables': {
'rm' : '<!(node -e "require(\'addon-tools-raub\').rm()")',
'cp' : '<!(node -e "require(\'addon-tools-raub\').cp()")',
'mkdir' : '<!(node -e "require(\'addon-tools-raub\').mkdir()")',
},
```
```
'variables': {
'rm' : '<!(node -e "require(\'addon-tools-raub\').rm()")',
'cp' : '<!(node -e "require(\'addon-tools-raub\').cp()")',
'mkdir' : '<!(node -e "require(\'addon-tools-raub\').mkdir()")',
},
```
On both Windows and Unix those are the console commands for various
file system operations. No need for GYP conditions, yay!
</details>
On both Windows and Unix those are the console commands for various
file system operations. No need for GYP conditions, yay!
<details>
### Addon binary directory
<summary>Addon binary directory</summary>
```
'variables': {
'binary' : '<!(node -e "require(\'addon-tools-raub\').bin()")',
},
```
In some cases, you'd like to have your addon installed for multiple architectures
simultaneously. For example, when using NVM to fluently switch environments.
Because the target directory is different for each arch, you only have to do
`npm rebuild` after the first switch.
</details>
```
'variables': {
'binary' : '<!(node -e "require(\'addon-tools-raub\').bin()")',
},
```
<details>
### Include directories
<summary>Include directories</summary>
```
'include_dirs': [
'<!@(node -p "require(\'addon-tools-raub\').include")',
],
```
```
'include_dirs': [
'<!@(node -e "require(\'addon-tools-raub\').include()")',
],
```
Those are the directory paths to C++ include files for Addon Tools and Nan
(which is preinstalled with Addon Tools)
</details>
<details>
<summary>Remove intermediates</summary>
```
[ 'OS=="linux"', { 'action' : [
'<(rm)',
'<(module_root_dir)/build/Release/obj.target/addon/cpp/addon.o',
'<(module_root_dir)/build/Release/addon.node'
] } ],
[ 'OS=="mac"', { 'action' : [
'<(rm)',
'<(module_root_dir)/build/Release/obj.target/addon/cpp/addon.o',
'<(module_root_dir)/build/Release/addon.node'
] } ],
[ 'OS=="win"', { 'action' : [
'<(rm)',
'<(module_root_dir)/build/Release/addon.*',
'<(module_root_dir)/build/Release/obj/addon/*.*'
] } ],
```
Build-files can be removed in a separate build-step with `<(rm)`. Those are
usually PDB and OBJ files, which are rather big. However, in case of a hardcore
debug session you might want to comment this out.
</details>
Those are the directory paths to C++ include files for Addon Tools and Napi
(which is preinstalled with Addon Tools)
### Binary dependency package
@ -137,7 +92,6 @@ would encourage you to abide by the following rules:
* Your binary directories are:
* bin-win32
* bin-win64
* bin-linux64
* bin-mac64

28
download.js Normal file
View File

@ -0,0 +1,28 @@
'use strict';
const https = require('https');
const http = require('http');
const WritableBuffer = require('./writable-buffer');
const protocols = { http, https };
module.exports = url => new Promise((res, rej) => {
url = url.toLowerCase();
const stream = new WritableBuffer();
const proto = protocols[url.match(/^https?/)[0]];
proto.get(url, response => {
response.pipe(stream);
response.on('end', () => res(stream.get()));
response.on('error', err => rej(err));
});
});

View File

@ -1,116 +0,0 @@
{
"root": true,
"env": {
"node" : true,
"es6" : true,
"mocha" : true
},
"globals": {
"expect" : true,
"chai" : true,
"sinon" : true
},
"extends": ["eslint:recommended"],
"parserOptions": {
"ecmaVersion": 8,
"ecmaFeatures": {
"experimentalObjectRestSpread": true
}
},
"rules": {
"arrow-parens": ["error", "as-needed"],
"no-trailing-spaces": [
"error",
{
"skipBlankLines": true
}
],
"indent": [
"error",
"tab",
{
"SwitchCase": 1
}
],
"linebreak-style": [
"error",
"unix"
],
"max-len": ["error", 110],
"quotes": [
"error",
"single"
],
"semi": [
"error",
"always"
],
"no-multiple-empty-lines": ["error", { "max": 3, "maxEOF": 1, "maxBOF": 1 }],
"keyword-spacing": ["error", { "before": true, "after": true }],
"space-before-blocks": ["error"],
"space-before-function-paren": ["error", {"anonymous": "always", "named": "never", "asyncArrow": "always"}],
"space-infix-ops": ["error"],
"space-unary-ops": [
"error", {
"words": true,
"nonwords": false,
"overrides": {
"!": true
}
}
],
"spaced-comment": [0],
"camelcase": ["error"],
"no-tabs": [0],
"comma-dangle": [0],
"global-require": [0],
"func-names": [0],
"no-param-reassign": [0],
"no-underscore-dangle": [0],
"no-restricted-syntax": [
"error",
{
"selector": "LabeledStatement",
"message": "Labels are a form of GOTO; using them makes code confusing and hard to maintain and understand."
},
{
"selector": "WithStatement",
"message": "`with` is disallowed in strict mode because it makes code impossible to predict and optimize."
}
],
"no-mixed-operators": [0],
"no-plusplus": [0],
"comma-spacing": [0],
"default-case": [0],
"no-shadow": [0],
"no-console": [0],
"key-spacing": [0],
"no-return-assign": [0],
"consistent-return": [0],
"class-methods-use-this": [0],
"no-multi-spaces": [
"error",
{
"exceptions": {
"VariableDeclarator": true,
"Property": true,
"ImportDeclaration": true
}
}
],
"array-callback-return": [0],
"no-use-before-define": [
"error",
{
"functions": false,
"classes": true,
"variables": true
}
],
"padded-blocks": [0],
"space-in-parens": [0],
"valid-jsdoc": [0],
"no-unused-expressions": [0],
"import/no-dynamic-require": [0]
}
}

View File

@ -1,12 +0,0 @@
.idea
.cproject
.project
.lock-wscript
build*/
bin-*/
.DS_Store
Debug/
node_modules/
package-lock.json
binary/
*.log

View File

@ -1,16 +0,0 @@
*.log
.cproject
.eslintrc
.gitignore
.idea
.lock-wscript
.project
binary/
bin-*/
build*/
CPPLINT.cfg
Debug/
examples/
package-lock.json
test/
qt/

View File

@ -1,15 +0,0 @@
set noparent
linelength=110
filter=-legal/copyright
filter=-build/include_order
filter=-build/header_guard
filter=-build/namespaces
filter=-build/include_what_you_use
filter=-whitespace/blank_line
filter=-whitespace/comments
filter=-whitespace/tab
filter=-whitespace/end_of_line
filter=-whitespace/indent
filter=-whitespace/operators
filter=-whitespace/parens
filter=-readability/todo

View File

@ -1,91 +0,0 @@
{
'variables': {
'rm' : '<!(node -e "require(\'addon-tools-raub\').rm()")',
'cp' : '<!(node -e "require(\'addon-tools-raub\').cp()")',
'mkdir' : '<!(node -e "require(\'addon-tools-raub\').mkdir()")',
'binary' : '<!(node -e "require(\'addon-tools-raub\').bin()")',
},
'targets': [
{
'target_name': 'addon',
'sources': [
'cpp/bindings.cpp',
'cpp/example.cpp',
],
'include_dirs': [
'<!@(node -e "require(\'addon-tools-raub\').include()")',
],
'conditions' : [
[
'OS=="win"',
{
'msvs_settings' : {
'VCCLCompilerTool' : {
'AdditionalOptions' : [
'/O2','/Oy', # Comment this for debugging
# '/Z7', # Unomment this for debugging
'/GL','/GF','/Gm-', '/Fm-',
'/EHsc','/MT','/GS','/Gy','/GR-','/Gd',
]
},
'VCLinkerTool' : {
'AdditionalOptions' : ['/RELEASE','/OPT:REF','/OPT:ICF','/LTCG']
},
},
},
],
],
},
{
'target_name' : 'make_directory',
'type' : 'none',
'dependencies' : ['addon'],
'actions' : [{
'action_name' : 'Directory created.',
'inputs' : [],
'outputs' : ['build'],
'action': ['<(mkdir)', '-p', '<(binary)']
}],
},
{
'target_name' : 'copy_binary',
'type' : 'none',
'dependencies' : ['make_directory'],
'actions' : [{
'action_name' : 'Module copied.',
'inputs' : [],
'outputs' : ['binary'],
'action' : ['<(cp)', 'build/Release/addon.node', '<(binary)/addon.node'],
}],
},
{
'target_name' : 'remove_extras',
'type' : 'none',
'dependencies' : ['copy_binary'],
'actions' : [{
'action_name' : 'Build intermediates removed.',
'inputs' : [],
'outputs' : ['cpp'],
'conditions' : [
[ 'OS=="linux"', { 'action' : [
'rm',
'<(module_root_dir)/build/Release/obj.target/addon/cpp/bindings.o',
'<(module_root_dir)/build/Release/obj.target/addon/cpp/example.o',
'<(module_root_dir)/build/Release/addon.node'
] } ],
[ 'OS=="mac"', { 'action' : [
'rm',
'<(module_root_dir)/build/Release/obj.target/addon/cpp/bindings.o',
'<(module_root_dir)/build/Release/obj.target/addon/cpp/example.o',
'<(module_root_dir)/build/Release/addon.node'
] } ],
[ 'OS=="win"', { 'action' : [
'<(rm)',
'<(module_root_dir)/build/Release/addon.*',
'<(module_root_dir)/build/Release/obj/addon/*.*'
] } ],
],
}],
},
]
}

View File

@ -1,17 +0,0 @@
'use strict';
const util = require('util');
const { binPath } = require('addon-tools-raub');
const core = require(`./${binPath}/addon`);
const { Example } = core;
Example.prototype[util.inspect.custom] = function () {
return `Example { listeners: [${this.eventNames()}] }`;
};
module.exports = core;

View File

@ -1,23 +0,0 @@
#include <cstdlib>
#include <event-emitter.hpp>
#include "example.hpp"
extern "C" {
void init(V8_VAR_OBJ target) {
EventEmitter::init(target);
Example::init(target);
}
NODE_MODULE(example, init);
} // extern "C"

View File

@ -1,105 +0,0 @@
#include <cstdlib>
#include "example.hpp"
using namespace v8;
using namespace node;
using namespace std;
// ------ Aux macros
#define THIS_EXAMPLE \
Example *example = ObjectWrap::Unwrap<Example>(info.This());
#define THIS_CHECK \
if (example->_isDestroyed) return;
// ------ Constructor and Destructor
Example::Example() : EventEmitter() {
_isDestroyed = false;
}
Example::~Example() {
_destroy();
}
NAN_METHOD(Example::cppOn) { THIS_EXAMPLE; THIS_CHECK;
REQ_STR_ARG(0, name);
REQ_FUN_ARG(1, cb);
example->on(*name, cb);
}
// ------ System methods and props for ObjectWrap
V8_STORE_FT Example::_protoExample;
V8_STORE_FUNC Example::_ctorExample;
void Example::init(V8_VAR_OBJ target) {
V8_VAR_FT proto = Nan::New<FunctionTemplate>(newCtor);
// class AudioBufferSourceNode inherits AudioScheduledSourceNode
V8_VAR_FT parent = Nan::New(EventEmitter::_protoEventEmitter);
proto->Inherit(parent);
proto->InstanceTemplate()->SetInternalFieldCount(1);
proto->SetClassName(JS_STR("Example"));
// -------- dynamic
Nan::SetPrototypeMethod(proto, "destroy", destroy);
Nan::SetPrototypeMethod(proto, "cppOn", cppOn);
// -------- static
V8_VAR_FUNC ctor = Nan::GetFunction(proto).ToLocalChecked();
_protoExample.Reset(proto);
_ctorExample.Reset(ctor);
Nan::Set(target, JS_STR("Example"), ctor);
}
NAN_METHOD(Example::newCtor) {
CTOR_CHECK("EventEmitter");
Example *example = new Example();
example->Wrap(info.This());
RET_VALUE(info.This());
}
void Example::_destroy() { DES_CHECK;
_isDestroyed = true;
EventEmitter::_destroy();
}
NAN_METHOD(Example::destroy) { THIS_EXAMPLE; THIS_CHECK;
example->emit("destroy");
example->_destroy();
}

View File

@ -1,39 +0,0 @@
#ifndef _EXAMPLE_HPP_
#define _EXAMPLE_HPP_
#include <event-emitter.hpp>
class Example : public EventEmitter {
public:
~Example();
static void init(V8_VAR_OBJ target);
protected:
Example();
void _destroy();
static V8_STORE_FT _protoExample;
static V8_STORE_FUNC _ctorExample;
bool _isDestroyed;
private:
static NAN_METHOD(newCtor);
static NAN_METHOD(destroy);
static NAN_METHOD(cppOn);
};
#endif // _EXAMPLE_HPP_

View File

@ -1,65 +0,0 @@
'use strict';
const { Example, EventEmitter } = require('./core');
console.log('Example', Example);
const example = new Example();
console.log('example 0', example, 'instanceof EventEmitter', example instanceof EventEmitter);
console.log('static listenerCount', EventEmitter.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.eventNames());
});
example.once('evt2', (arg1, arg2) => {
console.log('EVT2', arg1, arg2, example.eventNames());
});
example.emit('evt1', 111, '221');
example.emit('evt1', 112, '222');
console.log('example.eventNames 1', example.eventNames());
example.emit('evt2', 111, '221');
console.log('example.eventNames 2', example.eventNames());
example.emit('evt2', 112, '222');
console.log('example 1', example);
example.setMaxListeners(2);
example.on('max1', () => {});
example.on('max1', () => {});
example.on('max1', () => {});
example.on('cpp-on', (arg1, arg2) => {
console.log('CPP_ON', arg1, arg2, example.eventNames());
});
example.emit('cpp-on', 555, 'abc');
module.exports = Example;

View File

@ -1,9 +0,0 @@
{
"name": "example",
"version": "0.0.0",
"private": true,
"main": "index.js",
"dependencies": {
"addon-tools-raub": "https://github.com/node-3d/addon-tools-raub.git"
}
}

View File

@ -1,9 +0,0 @@
node_modules
*.log
build*
.DS_Store
*.pro.user
*.exp
*.pdb
*.ilk
package-lock.json

View File

@ -1,11 +0,0 @@
node_modules
*.log
build*
.DS_Store
*.pro.user
*.exp
*.pdb
*.ilk
.gitignore
package-lock.json
test

View File

@ -1,21 +0,0 @@
{
'variables': {
'rm' : '<!(node -e "require(\'addon-tools-raub\').rm()")',
'rem' : '<!(node -e "require(\'.\').rem()")',
'XALL%': 'false',
},
'targets': [
{
'target_name' : 'remove_extras',
'type' : 'none',
'conditions' : [['XALL=="false"', {'actions': [
{
'action_name' : 'Unnecessary binaries removed.',
'inputs' : [],
'outputs' : ['build'],
'action' : ['<(rm)', '-rf', '<@(rem)'],
}
]}]],
}
]
}

View File

@ -1,3 +0,0 @@
'use strict';
module.exports = require('addon-tools-raub').paths(__dirname);

View File

@ -1,9 +0,0 @@
{
"name": "example",
"version": "0.0.0",
"private": true,
"main": "index.js",
"dependencies": {
"addon-tools-raub": "https://github.com/node-3d/addon-tools-raub.git"
}
}

View File

@ -2,251 +2,315 @@
#define _ADDON_TOOLS_HPP_
#include <nan.h>
#include <napi.h>
#define NAN_HS Nan::HandleScope scope;
#define NAPI_ENV Napi::Env env = info.Env();
#define NAPI_HS Napi::HandleScope scope(env);
#define RET_VALUE(VAL) info.GetReturnValue().Set(VAL);
#define RET_UNDEFINED RET_VALUE(Nan::Undefined());
#define JS_STR(val) Napi::String::New(env, val)
#define JS_NUM(val) Napi::Number::New(env, static_cast<double>(val))
#define JS_EXT(val) Napi::External::New(env, reinterpret_cast<void*>(val))
#define JS_BOOL(val) Napi::Boolean::New(env, val)
#define JS_FUN(val) Napi::Function::Function(env, val)
#define JS_OBJ(val) Napi::Object::Object(env, val)
typedef v8::Local<v8::Value> V8_VAR_VAL;
typedef v8::Local<v8::Object> V8_VAR_OBJ;
typedef v8::Local<v8::Array> V8_VAR_ARR;
typedef v8::Local<v8::ArrayBufferView> V8_VAR_ABV;
typedef v8::Local<v8::String> V8_VAR_STR;
typedef v8::Local<v8::Function> V8_VAR_FUNC;
typedef v8::Local<v8::External> V8_VAR_EXT;
typedef v8::Local<v8::FunctionTemplate> V8_VAR_FT;
typedef v8::Local<v8::ObjectTemplate> V8_VAR_OT;
typedef Nan::Persistent<v8::FunctionTemplate> V8_STORE_FT;
typedef Nan::Persistent<v8::Function> V8_STORE_FUNC;
typedef Nan::Persistent<v8::Object> V8_STORE_OBJ;
typedef Nan::Persistent<v8::Value> V8_STORE_VAL;
#define JS_STR(...) Nan::New<v8::String>(__VA_ARGS__).ToLocalChecked()
#define JS_UTF8(...) Nan::New<v8::String>(__VA_ARGS__).ToLocalChecked()
#define JS_INT(val) Nan::New<v8::Integer>(val)
#define JS_INT32(val) Nan::New<v8::Integer>(val)
#define JS_UINT32(val) Nan::New<v8::Integer>(val)
#define JS_NUM(val) Nan::New<v8::Number>(val)
#define JS_OFFS(val) Nan::New<v8::Number>(static_cast<double>(val))
#define JS_FLOAT(val) Nan::New<v8::Number>(val)
#define JS_DOUBLE(val) Nan::New<v8::Number>(val)
#define JS_EXT(val) Nan::New<v8::External>(reinterpret_cast<void*>(val))
#define JS_BOOL(val) (val) ? Nan::True() : Nan::False()
#define JS_FUN(val) Nan::New<v8::Function>(val)
#define JS_OBJ(val) Nan::New<v8::Object>(val)
#define RET_STR(...) RET_VALUE(JS_STR(__VA_ARGS__))
#define RET_UTF8(...) RET_VALUE(JS_UTF8(__VA_ARGS__))
#define RET_INT(val) RET_VALUE(JS_INT(val))
#define RET_INT32(val) RET_VALUE(JS_INT32(val))
#define RET_UINT32(val) RET_VALUE(JS_UINT32(val))
#define RET_VALUE(VAL) return VAL;
#define RET_UNDEFINED RET_VALUE(env.Undefined())
#define RET_NULL RET_VALUE(env.Null())
#define RET_STR(val) RET_VALUE(JS_STR(val))
#define RET_NUM(val) RET_VALUE(JS_NUM(val))
#define RET_OFFS(val) RET_VALUE(JS_OFFS(val))
#define RET_FLOAT(val) RET_VALUE(JS_FLOAT(val))
#define RET_DOUBLE(val) RET_VALUE(JS_DOUBLE(val))
#define RET_EXT(val) RET_VALUE(JS_EXT(val))
#define RET_BOOL(val) RET_VALUE(JS_BOOL(val))
#define RET_FUN(val) RET_VALUE(JS_FUN(val))
#define RET_OBJ(val) RET_VALUE(JS_OBJ(val))
#define JS_THROW(val) \
Napi::Error::New(env, val).ThrowAsJavaScriptException();
#define REQ_ARGS(N) \
if (info.Length() < (N)) \
return Nan::ThrowTypeError("Expected at least " #N " arguments");
if (info.Length() < (N)) { \
JS_THROW("Expected at least " #N " arguments"); \
}
#define IS_ARG_EMPTY(I) (info[I]->IsNull() || info[I]->IsUndefined())
#define IS_EMPTY(val) (val.IsNull() || val.IsUndefined())
#define IS_ARG_EMPTY(I) IS_EMPTY(info[I])
#define CHECK_REQ_ARG(I, C, T) \
if (info.Length() <= (I) || ! info[I]->C) \
return Nan::ThrowTypeError("Argument " #I " must be " T);
if (info.Length() <= (I) || ! info[I].C) { \
JS_THROW("Argument " #I " must be of type `" T "`"); \
}
#define CHECK_LET_ARG(I, C, T) \
if ( ! (IS_ARG_EMPTY(I) || info[I]->C) ) \
return Nan::ThrowTypeError("Argument " #I " must be " T " or null");
if ( ! (IS_ARG_EMPTY(I) || info[I].C) ) { \
JS_THROW( \
"Argument " #I \
" must be of type `" T \
"` or be `null`/`undefined`" \
); \
}
#define REQ_UTF8_ARG(I, VAR) \
CHECK_REQ_ARG(I, IsString(), "string"); \
Nan::Utf8String VAR(info[I]);
#define REQ_STR_ARG(I, VAR) \
CHECK_REQ_ARG(I, IsString(), "String"); \
std::string VAR = info[I].ToString().Utf8Value();
#define LET_UTF8_ARG(I, VAR) \
CHECK_LET_ARG(I, IsString(), "string"); \
Nan::Utf8String VAR(JS_STR(""));
#define USE_STR_ARG(I, VAR, DEF) \
CHECK_LET_ARG(I, IsString(), "String"); \
std::string VAR = IS_ARG_EMPTY(I) ? (DEF) : info[I].ToString().Utf8Value();
#define LET_STR_ARG(I, VAR) USE_STR_ARG(I, VAR, "")
#define REQ_STR_ARG(I, VAR) REQ_UTF8_ARG(I, VAR)
#define LET_STR_ARG(I, VAR) LET_UTF8_ARG(I, VAR)
#define REQ_INT32_ARG(I, VAR) \
CHECK_REQ_ARG(I, IsInt32(), "int32"); \
int VAR = info[I].As<v8::Int32>()->Value();
CHECK_REQ_ARG(I, IsNumber(), "Int32"); \
int VAR = info[I].ToNumber().Int32Value();
#define USE_INT32_ARG(I, VAR, DEF) \
CHECK_LET_ARG(I, IsNumber(), "Int32"); \
int VAR = IS_ARG_EMPTY(I) ? (DEF) : info[I].Int32Value();
#define LET_INT32_ARG(I, VAR) USE_INT32_ARG(I, VAR, 0)
#define REQ_INT_ARG(I, VAR) REQ_INT32_ARG(I, VAR)
#define USE_INT_ARG(I, VAR, DEF) USE_INT32_ARG(I, VAR, DEF)
#define LET_INT_ARG(I, VAR) LET_INT32_ARG(I, VAR)
#define LET_INT32_ARG(I, VAR) \
CHECK_LET_ARG(I, IsInt32(), "int32"); \
int VAR = IS_ARG_EMPTY(I) ? 0 : info[I].As<v8::Int32>()->Value();
#define REQ_UINT32_ARG(I, VAR) \
CHECK_REQ_ARG(I, IsUint32(), "uint32"); \
unsigned int VAR = info[I].As<v8::Uint32>()->Value();
CHECK_REQ_ARG(I, IsNumber(), "Uint32"); \
unsigned int VAR = info[I].ToNumber().Uint32Value();
#define LET_UINT32_ARG(I, VAR) \
CHECK_LET_ARG(I, IsUint32(), "uint32"); \
unsigned int VAR = IS_ARG_EMPTY(I) ? 0 : info[I].As<v8::Uint32>()->Value();
#define USE_UINT32_ARG(I, VAR, DEF) \
CHECK_LET_ARG(I, IsNumber(), "Uint32"); \
unsigned int VAR = IS_ARG_EMPTY(I) \
? (DEF) \
: info[I].ToNumber().Uint32Value();
#define LET_UINT32_ARG(I, VAR) USE_UINT32_ARG(I, VAR, 0)
#define REQ_UINT_ARG(I, VAR) REQ_UINT_ARG(I, VAR)
#define USE_UINT_ARG(I, VAR, DEF) USE_UINT32_ARG(I, VAR, DEF)
#define LET_UINT_ARG(I, VAR) LET_UINT32_ARG(I, VAR)
#define REQ_INT_ARG(I, VAR) LET_INT32_ARG(I, VAR)
#define LET_INT_ARG(I, VAR) REQ_UINT32_ARG(I, VAR)
#define REQ_BOOL_ARG(I, VAR) \
CHECK_REQ_ARG(I, IsBoolean(), "bool"); \
bool VAR = info[I].As<v8::Boolean>()->Value();
CHECK_REQ_ARG(I, IsBoolean(), "Bool"); \
bool VAR = info[I].ToBoolean().Value();
#define USE_BOOL_ARG(I, VAR, DEF) \
CHECK_LET_ARG(I, IsBoolean(), "Bool"); \
bool VAR = IS_ARG_EMPTY(I) ? (DEF) : info[I].ToBoolean().Value();
#define LET_BOOL_ARG(I, VAR) USE_BOOL_ARG(I, VAR, false)
#define LET_BOOL_ARG(I, VAR) \
CHECK_LET_ARG(I, IsBoolean(), "bool"); \
bool VAR = IS_ARG_EMPTY(I) ? false : info[I].As<v8::Boolean>()->Value();
#define REQ_OFFS_ARG(I, VAR) \
CHECK_REQ_ARG(I, IsNumber(), "number"); \
size_t VAR = static_cast<size_t>(info[I].As<v8::Integer>()->Value());
CHECK_REQ_ARG(I, IsNumber(), "Number"); \
size_t VAR = static_cast<size_t>(info[I].ToNumber().DoubleValue());
#define LET_OFFS_ARG(I, VAR) \
CHECK_LET_ARG(I, IsNumber(), "number"); \
size_t VAR = IS_ARG_EMPTY(I) ? 0 : static_cast<size_t>( \
info[I].As<v8::Integer>()->Value() \
);
#define USE_OFFS_ARG(I, VAR, DEF) \
CHECK_LET_ARG(I, IsNumber(), "Number"); \
size_t VAR = IS_ARG_EMPTY(I) \
? (DEF) \
: static_cast<size_t>(info[I].ToNumber().DoubleValue());
#define LET_OFFS_ARG(I, VAR) USE_OFFS_ARG(I, VAR, 0)
#define REQ_DOUBLE_ARG(I, VAR) \
CHECK_REQ_ARG(I, IsNumber(), "number"); \
double VAR = info[I].As<v8::Number>()->Value();
CHECK_REQ_ARG(I, IsNumber(), "Number"); \
double VAR = info[I].ToNumber().DoubleValue();
#define LET_DOUBLE_ARG(I, VAR) \
CHECK_LET_ARG(I, IsNumber(), "number"); \
double VAR = IS_ARG_EMPTY(I) ? 0.0 : info[I].As<v8::Number>()->Value();
#define USE_DOUBLE_ARG(I, VAR, DEF) \
CHECK_LET_ARG(I, IsNumber(), "Number"); \
double VAR = IS_ARG_EMPTY(I) ? (DEF) : info[I].ToNumber().DoubleValue();
#define LET_DOUBLE_ARG(I, VAR) USE_DOUBLE_ARG(I, VAR, 0.0)
#define REQ_FLOAT_ARG(I, VAR) \
CHECK_REQ_ARG(I, IsNumber(), "number"); \
float VAR = static_cast<float>(info[I].As<v8::Number>()->Value());
CHECK_REQ_ARG(I, IsNumber(), "Number"); \
float VAR = info[I].ToNumber().FloatValue();
#define LET_FLOAT_ARG(I, VAR) \
CHECK_LET_ARG(I, IsNumber(), "number"); \
float VAR = IS_ARG_EMPTY(I) ? 0.f : static_cast<float>( \
info[I].As<v8::Number>()->Value() \
);
#define USE_FLOAT_ARG(I, VAR, DEF) \
CHECK_LET_ARG(I, IsNumber(), "Number"); \
float VAR = IS_ARG_EMPTY(I) ? (DEF) : info[I].ToNumber().FloatValue();
#define LET_FLOAT_ARG(I, VAR) USE_FLOAT_ARG(I, VAR, 0.f)
#define REQ_EXT_ARG(I, VAR) \
CHECK_REQ_ARG(I, IsExternal(), "void*"); \
V8_VAR_EXT VAR = V8_VAR_EXT::Cast(info[I]);
CHECK_REQ_ARG(I, IsExternal(), "Pointer"); \
Napi::External VAR = info[I].As<Napi::External>();
#define LET_EXT_ARG(I, VAR) \
CHECK_LET_ARG(I, IsExternal(), "number"); \
V8_VAR_EXT VAR = IS_ARG_EMPTY(I) ? JS_EXT(nullptr) : V8_VAR_EXT::Cast(info[I]);
#define USE_EXT_ARG(I, VAR, DEF) \
CHECK_LET_ARG(I, IsExternal(), "Pointer"); \
Napi::External VAR = IS_ARG_EMPTY(I) ? (DEF) : info[I].As<Napi::External>();
#define LET_EXT_ARG(I, VAR) USE_EXT_ARG(I, VAR, JS_EXT(nullptr))
#define REQ_FUN_ARG(I, VAR) \
CHECK_REQ_ARG(I, IsFunction(), "function"); \
V8_VAR_FUNC VAR = V8_VAR_FUNC::Cast(info[I]);
CHECK_REQ_ARG(I, IsFunction(), "Function"); \
Napi::Function VAR = info[I].As<Napi::Function>();
#define REQ_OBJ_ARG(I, VAR) \
CHECK_REQ_ARG(I, IsObject(), "object"); \
V8_VAR_OBJ VAR = V8_VAR_OBJ::Cast(info[I]);
CHECK_REQ_ARG(I, IsObject(), "Object"); \
Napi::Object VAR = info[I].As<Napi::Object>();
#define USE_OBJ_ARG(I, VAR, DEF) \
CHECK_LET_ARG(I, IsObject(), "Object"); \
Napi::Object VAR = IS_ARG_EMPTY(I) ? (DEF) : info[I].As<Napi::Object>();
#define LET_OBJ_ARG(I, VAR) USE_OBJ_ARG(I, VAR, info[I].As<Napi::Object>())
#define REQ_OBJ_ARG(I, VAR) \
CHECK_REQ_ARG(I, IsObject(), "Object"); \
Napi::Object VAR = info[I].As<Napi::Object>();
#define USE_OBJ_ARG(I, VAR, DEF) \
CHECK_LET_ARG(I, IsObject(), "Object"); \
Napi::Object VAR = IS_ARG_EMPTY(I) ? (DEF) : info[I].As<Napi::Object>();
#define LET_OBJ_ARG(I, VAR) USE_OBJ_ARG(I, VAR, info[I].As<Napi::Object>())
#define REQ_ARRV_ARG(I, VAR) \
REQ_OBJ_ARG(I, _obj_##VAR); \
if( ! _obj_##VAR->IsArrayBufferView() ) \
return Nan::ThrowTypeError("Argument " #I " must be an array buffer");\
V8_VAR_ABV VAR = V8_VAR_ABV::Cast(_obj_##VAR);
CHECK_REQ_ARG(I, IsArrayBuffer(), "Object"); \
Napi::ArrayBuffer VAR = info[I].As<Napi::ArrayBuffer>();
#define SET_PROP(OBJ, KEY, VAL) OBJ->Set(JS_STR(KEY), VAL);
#define SET_I(ARR, I, VAL) ARR->Set(I, VAL);
#define REQ_BUF_ARG(I, VAR) \
CHECK_REQ_ARG(I, IsBuffer(), "Buffer"); \
Napi::Buffer<uint8_t> VAR = info[I].As< Napi::Buffer<uint8_t> >();
#define CTOR_CHECK(T) \
if ( ! info.IsConstructCall() ) \
return Nan::ThrowTypeError(T " must be called with the 'new' keyword.");
JS_THROW(T " must be called with the 'new' keyword.");
#define DES_CHECK \
if (_isDestroyed) return;
#define THIS_CHECK \
NAPI_ENV; \
if (_isDestroyed) RET_UNDEFINED;
#define SETTER_CHECK(C, T) \
if ( ! value->C ) \
return Nan::ThrowTypeError("Value must be " T);
if ( ! value.C ) \
JS_THROW("Value must be " T);
#define ACCESSOR_RW(OBJ, NAME) \
Nan::SetAccessor(OBJ, JS_STR(#NAME), NAME ## Getter, NAME ## Setter);
#define JS_METHOD(NAME) Napi::Value NAME(const Napi::CallbackInfo &info)
#define JS_GETTER(NAME) Napi::Value NAME(const Napi::CallbackInfo &info)
#define JS_SETTER(NAME) \
void NAME(const Napi::CallbackInfo &info, const Napi::Value &value)
#define ACCESSOR_R(OBJ, NAME) \
Nan::SetAccessor(OBJ, JS_STR(#NAME), NAME ## Getter);
#define ACCESSOR_RW(CLASS, NAME) \
InstanceAccessor(#NAME, &CLASS::NAME ## Getter, &CLASS::NAME ## Setter)
#define ACCESSOR_R(CLASS, NAME) \
InstanceAccessor(#NAME, &CLASS::NAME ## Getter, nullptr)
#define ACCESSOR_M(CLASS, NAME) \
InstanceMethod(#NAME, &CLASS::NAME)
#define SETTER_UTF8_ARG \
SETTER_CHECK(IsString(), "string"); \
Nan::Utf8String v(value);
#define SETTER_STR_ARG SETTER_UTF8_ARG
#define SETTER_STR_ARG \
SETTER_CHECK(IsNumber(), "String"); \
std::string v = value.ToString().Utf8Value();
#define SETTER_INT32_ARG \
SETTER_CHECK(IsInt32(), "int32"); \
int v = value.As<v8::Int32>()->Value();
SETTER_CHECK(IsNumber(), "Int32"); \
int v = value.ToNumber().Int32Value();
#define SETTER_INT_ARG SETTER_INT32_ARG
#define SETTER_BOOL_ARG \
SETTER_CHECK(IsBoolean(), "bool"); \
bool v = value.As<v8::Boolean>()->Value();
SETTER_CHECK(IsBoolean(), "Bool"); \
bool v = value.ToBoolean().Value();
#define SETTER_UINT32_ARG \
SETTER_CHECK(IsUint32(), "uint32"); \
unsigned int v = value.As<v8::Uint32>()->Value();
SETTER_CHECK(IsNumber(), "Uint32"); \
unsigned int v = value.ToNumber().Uint32Value();
#define SETTER_UINT_ARG SETTER_UINT32_ARG
#define SETTER_OFFS_ARG \
SETTER_CHECK(IsNumber(), "number"); \
size_t v = static_cast<size_t>(value.As<v8::Integer>()->Value());
SETTER_CHECK(IsNumber(), "Number"); \
size_t v = static_cast<size_t>(value.ToNumber().DoubleValue());
#define SETTER_DOUBLE_ARG \
SETTER_CHECK(IsNumber(), "number"); \
double v = value.As<v8::Number>()->Value();
SETTER_CHECK(IsNumber(), "Number"); \
double v = value.ToNumber().DoubleValue();
#define SETTER_FLOAT_ARG \
SETTER_CHECK(IsNumber(), "number"); \
float v = static_cast<float>(value.As<v8::Number>()->Value());
SETTER_CHECK(IsNumber(), "Number"); \
float v = value.ToNumber().FloatValue();
#define SETTER_EXT_ARG \
SETTER_CHECK(IsExternal(), "void*"); \
V8_VAR_EXT v = V8_VAR_EXT::Cast(value);
SETTER_CHECK(IsExternal(), "Pointer"); \
Napi::External v = value.As<Napi::External>();
#define SETTER_FUN_ARG \
SETTER_CHECK(IsFunction(), "function"); \
V8_VAR_FUNC v = V8_VAR_FUNC::Cast(value);
SETTER_CHECK(IsFunction(), "Function"); \
Napi::Function v = value.As<Napi::Function>()
#define SETTER_OBJ_ARG \
SETTER_CHECK(IsObject(), "object"); \
V8_VAR_OBJ v = V8_VAR_OBJ::Cast(value);
SETTER_CHECK(IsObject(), "Object"); \
Napi::Object v = value.As<Napi::Object>()
#define SETTER_ARRV_ARG \
SETTER_CHECK(IsObject(), "object"); \
V8_VAR_OBJ _obj_v = V8_VAR_OBJ::Cast(value); \
if( ! _obj_v->IsArrayBufferView() ) \
return Nan::ThrowTypeError("The value must be an array buffer"); \
V8_VAR_ABV v = V8_VAR_ABV::Cast(_obj_v);
SETTER_CHECK(IsArrayBuffer(), "TypedArray"); \
Napi::ArrayBuffer v = value.As<Napi::ArrayBuffer>();
#define GET_AND_THROW_LAST_ERROR() \
do { \
const napi_extended_error_info *error_info; \
napi_get_last_error_info((env), &error_info); \
bool is_pending; \
napi_is_exception_pending((env), &is_pending); \
/* If an exception is already pending, don't rethrow it */ \
if (!is_pending) { \
const char* error_message = error_info->error_message != NULL \
? error_info->error_message \
: "empty error message"; \
JS_THROW(error_message); \
} \
} while (0)
#define NAPI_CALL(the_call, ATE) \
do { \
if ((the_call) != napi_ok) { \
GET_AND_THROW_LAST_ERROR(); \
ATE; \
} \
} while (0)
#define JS_RUN_3(code, VAR, ATE) \
napi_value __RESULT_ ## VAR; \
NAPI_CALL( \
napi_run_script(env, napi_value(JS_STR(code)), &__RESULT_ ## VAR), \
ATE \
); \
Napi::Value VAR(env, __RESULT_ ## VAR);
#define JS_RUN_2(code, VAR) JS_RUN_3(code, VAR, return)
#define JS_RUN JS_RUN_3
template<typename Type>
inline Type* getArrayData(V8_VAR_OBJ obj, int *num = nullptr) {
inline Type* getArrayData(Napi::Env env, Napi::Object obj, int *num = nullptr) {
Type *data = nullptr;
@ -254,32 +318,68 @@ inline Type* getArrayData(V8_VAR_OBJ obj, int *num = nullptr) {
*num = 0;
}
if ( ! obj->IsArrayBufferView() ) {
Nan::ThrowError("Argument must be a TypedArray.");
if ( ! obj.IsArrayBuffer() ) {
JS_THROW("Argument must be of type `TypedArray`.");
return data;
}
V8_VAR_ABV arr = V8_VAR_ABV::Cast(obj);
Napi::ArrayBuffer arr = obj.As<Napi::ArrayBuffer>();
if (num) {
*num = arr->ByteLength() / sizeof(Type);
*num = arr.ByteLength() / sizeof(Type);
}
data = reinterpret_cast<Type*>(arr->Buffer()->GetContents().Data());
data = static_cast<Type *>(arr.Data());
return data;
}
template<typename Type>
inline Type* getBufferData(Napi::Env env, Napi::Object obj, int *num = nullptr) {
Type *data = nullptr;
if (num) {
*num = 0;
}
if ( ! obj.IsBuffer() ) {
JS_THROW("Argument must be of type `Buffer`.");
return data;
}
Napi::Buffer<uint8_t> arr = obj.As< Napi::Buffer<uint8_t> >();
if (num) {
*num = arr.Length() / sizeof(Type);
}
data = arr.Data();
return data;
}
inline void *getData(V8_VAR_OBJ obj) {
inline void *getData(Napi::Env env, Napi::Object obj) {
void *pixels = nullptr;
if (obj->IsArrayBufferView()) {
pixels = getArrayData<unsigned char>(obj);
} else if (obj->Has(JS_STR("data"))) {
V8_VAR_VAL data = Nan::Get(obj, JS_STR("data")).ToLocalChecked();
if ( ! data->IsNullOrUndefined() ) {
pixels = node::Buffer::Data(data);
if (obj.IsArrayBuffer()) {
pixels = getArrayData<unsigned char>(env, obj);
} else if (obj.IsTypedArray()) {
pixels = getArrayData<unsigned char>(
env,
obj.As<Napi::TypedArray>().ArrayBuffer()
);
} else if (obj.Has("data")) {
Napi::Object data = obj.Get("data").As<Napi::Object>();
if (data.IsArrayBuffer()) {
pixels = getArrayData<unsigned char>(env, data);
} else if (data.IsBuffer()) {
pixels = getBufferData<unsigned char>(env, data);
} else if (data.IsTypedArray()) {
pixels = getArrayData<unsigned char>(
env,
data.As<Napi::TypedArray>().ArrayBuffer()
);
}
}
@ -288,28 +388,49 @@ inline void *getData(V8_VAR_OBJ obj) {
}
inline void consoleLog(int argc, V8_VAR_VAL *argv) {
inline void consoleLog(Napi::Env env, int argc, Napi::Value *argv) {
V8_VAR_STR code = JS_STR("((...args) => console.log(...args))");
JS_RUN_2("((...args) => console.log(...args))", log);
std::vector<napi_value> args;
for (int i = 0; i < argc; i++) {
args.push_back(napi_value(argv[i]));
}
v8::Local<v8::Value> log = v8::Script::Compile(
Nan::GetCurrentContext(), code
).ToLocalChecked()->Run(
Nan::GetCurrentContext()
).ToLocalChecked();
Nan::Callback logCb(Nan::To<v8::Function>(log).ToLocalChecked());
Nan::AsyncResource async("consoleLog()");
logCb.Call(argc, argv, &async);
log.As<Napi::Function>().Call(napi_value(env.Null()), args);
}
inline void consoleLog(const std::string &message) {
inline void consoleLog(Napi::Env env, const std::string &message) {
V8_VAR_VAL arg = JS_STR(message);
consoleLog(1, &arg);
Napi::Value arg = JS_STR(message);
consoleLog(env, 1, &arg);
}
inline void eventEmit(
Napi::Env env,
Napi::Object that,
const std::string &name,
int argc = 0,
Napi::Value *argv = nullptr
) {
if ( ! that.Has("emit") ) {
return;
}
Napi::String eventName = JS_STR(name);
Napi::Function thatEmit = that.Get("emit").As<Napi::Function>();
std::vector<napi_value> args;
args.push_back(napi_value(eventName));
for (int i = 0; i < argc; i++) {
args.push_back(napi_value(argv[i]));
}
thatEmit.Call(napi_value(that), args);
}

View File

@ -1,723 +0,0 @@
#ifndef _EVENT_EMITTER_
#define _EVENT_EMITTER_
#include <addon-tools.hpp>
#include <string>
#include <map>
#include <deque>
#define THIS_EVENT_EMITTER \
EventEmitter *eventEmitter = ObjectWrap::Unwrap<EventEmitter>(info.This());
#define EVENT_EMITTER_THIS_CHECK \
if (eventEmitter->_isDestroyed) return;
// This template class provides static-member initialization in-header
template <typename T>
class StaticHolder {
protected:
static V8_STORE_FT _protoEventEmitter;
static V8_STORE_FUNC _ctorEventEmitter;
};
template <typename T> V8_STORE_FT StaticHolder<T>::_protoEventEmitter;
template <typename T> V8_STORE_FUNC StaticHolder<T>::_ctorEventEmitter;
class EventEmitter : public StaticHolder<int>, public Nan::ObjectWrap {
typedef Nan::CopyablePersistentTraits<v8::Function>::CopyablePersistent FN_TYPE;
typedef std::deque<FN_TYPE> VEC_TYPE;
typedef std::map<std::string, VEC_TYPE> MAP_TYPE;
typedef std::map<int, FN_TYPE> FNMAP_TYPE;
typedef VEC_TYPE::iterator IT_TYPE;
typedef MAP_TYPE::iterator MAP_IT_TYPE;
typedef FNMAP_TYPE::iterator FNMAP_IT_TYPE;
public:
// Public V8 init
static void init(V8_VAR_OBJ target) {
V8_VAR_FT proto = Nan::New<v8::FunctionTemplate>(newCtor);
proto->InstanceTemplate()->SetInternalFieldCount(1);
proto->SetClassName(JS_STR("EventEmitter"));
// Accessors
V8_VAR_OT obj = proto->PrototypeTemplate();
ACCESSOR_R(obj, isDestroyed);
// -------- dynamic
Nan::SetPrototypeMethod(proto, "listenerCount", jsListenerCount);
Nan::SetPrototypeMethod(proto, "addEventListener", jsAddListener);
Nan::SetPrototypeMethod(proto, "addListener", jsAddListener);
Nan::SetPrototypeMethod(proto, "dispatchEvent", jsDispatchEvent);
Nan::SetPrototypeMethod(proto, "emit", jsEmit);
Nan::SetPrototypeMethod(proto, "eventNames", jsEventNames);
Nan::SetPrototypeMethod(proto, "getMaxListeners", jsGetMaxListeners);
Nan::SetPrototypeMethod(proto, "listeners", jsListeners);
Nan::SetPrototypeMethod(proto, "off", jsRemoveListener);
Nan::SetPrototypeMethod(proto, "on", jsAddListener);
Nan::SetPrototypeMethod(proto, "once", jsAddListener);
Nan::SetPrototypeMethod(proto, "prependListener", jsPrependListener);
Nan::SetPrototypeMethod(proto, "prependOnceListener", jsPrependOnceListener);
Nan::SetPrototypeMethod(proto, "removeAllListeners", jsRemoveAllListeners);
Nan::SetPrototypeMethod(proto, "removeEventListener", jsRemoveListener);
Nan::SetPrototypeMethod(proto, "removeListener", jsRemoveListener);
Nan::SetPrototypeMethod(proto, "setMaxListeners", jsSetMaxListeners);
Nan::SetPrototypeMethod(proto, "rawListeners", jsRawListeners);
Nan::SetPrototypeMethod(proto, "destroy", jsDestroy);
// -------- static
V8_VAR_FUNC ctor = Nan::GetFunction(proto).ToLocalChecked();
V8_VAR_OBJ ctorObj = V8_VAR_OBJ::Cast(ctor);
Nan::SetMethod(ctorObj, "listenerCount", jsStaticListenerCount);
_ctorEventEmitter.Reset(ctor);
_protoEventEmitter.Reset(proto);
Nan::Set(target, JS_STR("EventEmitter"), ctor);
}
// C++ side emit() method
void emit(const std::string &name, int argc = 0, V8_VAR_VAL *argv = NULL) {
// Important! As actual get map[key] produces a new (empty) map entry
if ( _listeners.find(name) == _listeners.end() ) {
return;
}
// A copy is intended, because handlers can call removeListener (and they DO)
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() ) {
Nan::AsyncResource async("EventEmitter::cpp_emit()");
callback.Call(handle(), argc, argv, &async);
}
}
}
// C++ side on() method
void on(const std::string &name, V8_VAR_FUNC cb) {
V8_VAR_OBJ me = handle();
V8_VAR_VAL onVal = Nan::Get(me, JS_STR("on")).ToLocalChecked();
V8_VAR_FUNC onFunc = V8_VAR_FUNC::Cast(onVal);
Nan::Callback onCb(onFunc);
V8_VAR_VAL argv[] = { JS_STR(name.c_str()), cb };
Nan::AsyncResource async("EventEmitter::cpp_on()");
onCb.Call(me, 2, argv, &async);
}
void _destroy() { DES_CHECK;
_isDestroyed = true;
_listeners.clear();
_raw.clear();
_wrappedIds.clear();
_rawIds.clear();
}
~EventEmitter () { _destroy(); }
protected:
EventEmitter () {
_isDestroyed = false;
_maxListeners = 0;
_freeId = 0;
}
private:
static NAN_METHOD(newCtor) {
CTOR_CHECK("EventEmitter");
EventEmitter *eventEmitter = new EventEmitter();
eventEmitter->Wrap(info.This());
RET_VALUE(info.This());
}
static NAN_GETTER(isDestroyedGetter) { THIS_EVENT_EMITTER; EVENT_EMITTER_THIS_CHECK;
RET_BOOL(eventEmitter->_isDestroyed);
}
// Deprecated static method
static NAN_METHOD(jsStaticListenerCount) {
REQ_OBJ_ARG(0, obj);
EventEmitter *eventEmitter = ObjectWrap::Unwrap<EventEmitter>(obj);
REQ_UTF8_ARG(1, name);
const VEC_TYPE &list = eventEmitter->_listeners[*name];
RET_INT(static_cast<int>(list.size()));
}
static NAN_METHOD(jsAddListener) {
_wrapListener(info);
RET_VALUE(info.This());
}
static NAN_METHOD(jsDispatchEvent) { THIS_EVENT_EMITTER; EVENT_EMITTER_THIS_CHECK;
REQ_OBJ_ARG(0, event);
if ( ! event->Has(JS_STR("type")) ) {
return Nan::ThrowError("Event must have the `type` property.");
}
Nan::Utf8String name(event->Get(JS_STR("type")));
V8_VAR_VAL args = event;
eventEmitter->emit(*name, 1, &args);
RET_BOOL(true);
}
static NAN_METHOD(jsEmit) { THIS_EVENT_EMITTER; EVENT_EMITTER_THIS_CHECK;
REQ_UTF8_ARG(0, name);
int length = info.Length();
std::vector< V8_VAR_VAL > args;
for (int i = 1; i < length; i++) {
args.push_back(info[i]);
}
eventEmitter->emit(*name, length - 1, &args[0]);
if ( eventEmitter->_listeners.find(*name) == eventEmitter->_listeners.end() ) {
RET_BOOL(false);
} else {
RET_BOOL(true);
}
}
static NAN_METHOD(jsEventNames) { THIS_EVENT_EMITTER; EVENT_EMITTER_THIS_CHECK;
V8_VAR_ARR jsNames = Nan::New<v8::Array>(eventEmitter->_raw.size());
if (eventEmitter->_raw.empty()) {
RET_VALUE(jsNames);
return;
}
int i = 0;
for (MAP_IT_TYPE it = eventEmitter->_raw.begin(); it != eventEmitter->_raw.end(); ++it, i++) {
jsNames->Set(JS_INT(i), JS_STR(it->first));
}
RET_VALUE(jsNames);
}
static NAN_METHOD(jsGetMaxListeners) { THIS_EVENT_EMITTER; EVENT_EMITTER_THIS_CHECK;
RET_INT(eventEmitter->_maxListeners);
}
static NAN_METHOD(jsListenerCount) { THIS_EVENT_EMITTER; EVENT_EMITTER_THIS_CHECK;
REQ_UTF8_ARG(0, name);
const VEC_TYPE &list = eventEmitter->_listeners[*name];
RET_INT(static_cast<int>(list.size()));
}
static NAN_METHOD(jsListeners) { THIS_EVENT_EMITTER; EVENT_EMITTER_THIS_CHECK;
REQ_UTF8_ARG(0, name);
VEC_TYPE &list = eventEmitter->_listeners[*name];
V8_VAR_ARR jsListeners = Nan::New<v8::Array>(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 _reportListeners(
const std::string &name,
int numListeners,
int maxListeners
) {
std::string msg = "EventEmitter Warning: too many listeners (";
msg += std::to_string(numListeners);
msg += " > ";
msg += std::to_string(maxListeners);
msg += ") on '";
msg += name;
msg += "' event, possible memory leak.\n";
// Some JS magic to retrieve the call stack
V8_VAR_STR code = JS_STR(
"(new Error()).stack.split('\\n').slice(2).join('\\n')"
);
v8::Local<v8::Value> stack = v8::Script::Compile(
Nan::GetCurrentContext(), code
).ToLocalChecked()->Run(
Nan::GetCurrentContext()
).ToLocalChecked();
Nan::Utf8String stackStr(stack);
msg += *stackStr;
consoleLog(msg);
}
static inline void _addListener(
const Nan::FunctionCallbackInfo<v8::Value> &info,
const std::string &name,
V8_STORE_FUNC *cb,
bool isFront
) { THIS_EVENT_EMITTER; EVENT_EMITTER_THIS_CHECK;
V8_VAR_VAL args[] = { info[0], info[1] };
eventEmitter->emit("newListener", 2, args);
if (isFront) {
eventEmitter->_listeners[name].push_front(*cb);
eventEmitter->_raw[name].push_front(*cb);
} else {
eventEmitter->_listeners[name].push_back(*cb);
eventEmitter->_raw[name].push_back(*cb);
}
int count = eventEmitter->_raw[name].size();
if (eventEmitter->_maxListeners > 0 && count > eventEmitter->_maxListeners) {
_reportListeners(name, count, eventEmitter->_maxListeners);
}
}
static inline void _wrapListener(
const Nan::FunctionCallbackInfo<v8::Value> &info,
bool isFront = false
) {
REQ_UTF8_ARG(0, name);
REQ_FUN_ARG(1, cb);
V8_STORE_FUNC persistentCb;
persistentCb.Reset(cb);
_addListener(info, *name, &persistentCb, isFront);
}
static inline void _addOnceListener(
const Nan::FunctionCallbackInfo<v8::Value> &info,
const std::string &name,
V8_STORE_FUNC *raw,
V8_STORE_FUNC *cb,
bool isFront
) { THIS_EVENT_EMITTER; EVENT_EMITTER_THIS_CHECK;
V8_VAR_VAL args[] = { info[0], info[1] };
eventEmitter->emit("newListener", 2, args);
if (isFront) {
eventEmitter->_listeners[name].push_front(*cb);
eventEmitter->_raw[name].push_front(*raw);
} else {
eventEmitter->_listeners[name].push_back(*cb);
eventEmitter->_raw[name].push_back(*raw);
}
int nextId = eventEmitter->_freeId++;
eventEmitter->_wrappedIds[nextId] = *cb;
eventEmitter->_rawIds[nextId] = *raw;
int count = eventEmitter->_raw[name].size();
if (eventEmitter->_maxListeners > 0 && count > eventEmitter->_maxListeners) {
_reportListeners(name, count, eventEmitter->_maxListeners);
}
}
static inline void _wrapOnceListener(
const Nan::FunctionCallbackInfo<v8::Value> &info,
bool isFront = false
) {
REQ_UTF8_ARG(0, name);
REQ_FUN_ARG(1, raw);
V8_VAR_STR code = JS_STR(R"(
((emitter, name, cb) => (...args) => {
cb(...args);
emitter.removeListener(name, cb);
})
)");
v8::Local<v8::Value> decor = v8::Script::Compile(
Nan::GetCurrentContext(), code
).ToLocalChecked()->Run(
Nan::GetCurrentContext()
).ToLocalChecked();
Nan::Callback decorCb(Nan::To<v8::Function>(decor).ToLocalChecked());
V8_VAR_VAL argv[] = { info.This(), info[0], raw };
Nan::AsyncResource async("EventEmitter::js_once()");
V8_VAR_VAL wrapValue = decorCb.Call(3, argv, &async).ToLocalChecked();
V8_VAR_FUNC wrap = V8_VAR_FUNC::Cast(wrapValue);
V8_STORE_FUNC persistentWrap;
persistentWrap.Reset(wrap);
V8_STORE_FUNC persistentRaw;
persistentRaw.Reset(raw);
_addOnceListener(info, *name, &persistentRaw, &persistentWrap, isFront);
}
static NAN_METHOD(jsOnce) {
_wrapOnceListener(info);
RET_VALUE(info.This());
}
static NAN_METHOD(jsPrependListener) {
_wrapListener(info, true);
RET_VALUE(info.This());
}
static NAN_METHOD(jsPrependOnceListener) {
_wrapOnceListener(info, true);
RET_VALUE(info.This());
}
static NAN_METHOD(jsRemoveAllListeners) { THIS_EVENT_EMITTER; EVENT_EMITTER_THIS_CHECK;
RET_VALUE(info.This());
if (info.Length() == 0) {
MAP_TYPE tmpMap = eventEmitter->_raw;
eventEmitter->_listeners.clear();
eventEmitter->_raw.clear();
eventEmitter->_wrappedIds.clear();
eventEmitter->_rawIds.clear();
for (MAP_IT_TYPE itMap = tmpMap.begin(); itMap != tmpMap.end(); ++itMap) {
const std::string &current = itMap->first;
VEC_TYPE &list = itMap->second;
for (IT_TYPE it = list.begin(); it != list.end(); ++it) {
V8_VAR_VAL args[] = { JS_STR(current.c_str()), Nan::New(*it) };
eventEmitter->emit("removeListener", 2, args);
}
}
return;
}
REQ_UTF8_ARG(0, n);
std::string name = std::string(*n);
VEC_TYPE &list = eventEmitter->_raw[name];
if (list.empty()) {
return;
}
if (eventEmitter->_rawIds.size()) {
std::vector<int> removes;
for (IT_TYPE it = list.begin(); it != list.end(); ++it) {
FN_TYPE fn = *it;
for (FNMAP_IT_TYPE itRaw = eventEmitter->_rawIds.begin(); itRaw != eventEmitter->_rawIds.end(); ++itRaw) {
if (fn == itRaw->second) {
removes.push_back(itRaw->first);
}
}
}
if (removes.size()) {
for (std::vector<int>::const_iterator it = removes.begin(); it != removes.end(); ++it) {
eventEmitter->_wrappedIds.erase(*it);
eventEmitter->_rawIds.erase(*it);
}
}
}
VEC_TYPE tmpVec = eventEmitter->_raw[name];
eventEmitter->_listeners[name].clear();
eventEmitter->_raw[name].clear();
for (IT_TYPE it = tmpVec.begin(); it != tmpVec.end(); ++it) {
V8_VAR_VAL args[] = { JS_STR(name.c_str()), Nan::New(*it) };
eventEmitter->emit("removeListener", 2, args);
}
}
static NAN_METHOD(jsRemoveListener) { THIS_EVENT_EMITTER; EVENT_EMITTER_THIS_CHECK;
RET_VALUE(info.This());
REQ_UTF8_ARG(0, n);
REQ_FUN_ARG(1, raw);
V8_STORE_FUNC persistentRaw;
persistentRaw.Reset(raw);
std::string name = std::string(*n);
VEC_TYPE &rawList = eventEmitter->_raw[name];
if (rawList.empty()) {
return;
}
V8_VAR_VAL args[] = { info[0], info[1] };
for (IT_TYPE it = rawList.begin(); it != rawList.end(); ++it) {
if (*it == persistentRaw) {
rawList.erase(it);
if (rawList.empty()) {
eventEmitter->_raw.erase(name);
}
break;
}
}
VEC_TYPE &wrapList = eventEmitter->_listeners[name];
if (eventEmitter->_wrappedIds.size() == 0) {
for (IT_TYPE it = wrapList.begin(); it != wrapList.end(); ++it) {
if (*it == persistentRaw) {
wrapList.erase(it);
if (wrapList.empty()) {
eventEmitter->_listeners.erase(name);
}
break;
}
}
eventEmitter->emit("removeListener", 2, args);
return;
}
for (FNMAP_IT_TYPE itRaw = eventEmitter->_rawIds.begin(); itRaw != eventEmitter->_rawIds.end(); ++itRaw) {
if (persistentRaw == itRaw->second) {
FN_TYPE fn = eventEmitter->_wrappedIds[itRaw->first];
for (IT_TYPE it = wrapList.begin(); it != wrapList.end(); ++it) {
if (*it == fn) {
wrapList.erase(it);
if (wrapList.empty()) {
eventEmitter->_listeners.erase(name);
}
break;
}
}
eventEmitter->_wrappedIds.erase(itRaw->first);
eventEmitter->_rawIds.erase(itRaw->first);
break;
}
}
eventEmitter->emit("removeListener", 2, args);
}
static NAN_METHOD(jsSetMaxListeners) { THIS_EVENT_EMITTER; EVENT_EMITTER_THIS_CHECK;
REQ_INT32_ARG(0, value);
eventEmitter->_maxListeners = value;
RET_VALUE(info.This());
}
static NAN_METHOD(jsRawListeners) { THIS_EVENT_EMITTER; EVENT_EMITTER_THIS_CHECK;
REQ_UTF8_ARG(0, name);
VEC_TYPE &list = eventEmitter->_raw[*name];
V8_VAR_ARR jsListeners = Nan::New<v8::Array>(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 NAN_METHOD(jsDestroy) { THIS_EVENT_EMITTER; EVENT_EMITTER_THIS_CHECK;
eventEmitter->emit("destroy");
eventEmitter->_destroy();
}
private:
bool _isDestroyed;
int _maxListeners;
MAP_TYPE _listeners;
MAP_TYPE _raw;
int _freeId;
FNMAP_TYPE _wrappedIds;
FNMAP_TYPE _rawIds;
};
#endif // _EVENT_EMITTER_

View File

@ -2,58 +2,43 @@
const path = require('path');
const download = require('./js/download');
const NAMES = ['win32', 'win64', 'linux64', 'mac64'];
const prefixName = name => `bin-${name}`;
const getPlatformDir = () => {
switch (process.platform) {
case 'win32' : return process.arch === 'x64' ? 'win64' : 'win32';
case 'linux' : return 'linux64';
case 'darwin' : return 'mac64';
default : throw new Error(`Platform "${process.platform}" is not supported.`);
}
const platformNames = {
win32 : 'win',
linux : 'lin',
darwin : 'osx',
};
const rootPath = __dirname.replace(/\\/g, '/');
const nanInclude = path.dirname(require.resolve('nan')).replace(/\\/g, '/');
const thisInclude = `${rootPath}/include`;
const platformName = platformNames[process.platform];
if ( ! platformName ) {
console.log(`Error: UNKNOWN PLATFORM "${process.platform}"`);
}
const isWindows = process.platform === 'win32';
const currentDir = prefixName(getPlatformDir());
const remDirs = NAMES.map(prefixName).filter(n => n !== currentDir);
const rootPath = __dirname.replace(/\\/g, '/');
const napiInclude = require('node-addon-api').include.replace(/\\/g, '/');
const thisInclude = `${rootPath}/include`;
const paths = dir => {
dir = dir.replace(/\\/g, '/');
const binPath = `${dir}/${currentDir}`;
const binPath = `${dir}/${platformName}`;
if (isWindows) {
process.env.path = `${binPath};${process.env.path ? `${process.env.path}` : ''}`;
}
const remPath = remDirs.map(k => `${dir}/${k}`).join(' ');
const includePath = `${dir}/include`;
return {
binPath,
remPath,
includePath,
bin() { console.log(binPath); },
rem() { console.log(remPath); },
include() { console.log(includePath); },
};
return { bin, include };
};
const includePath = `${nanInclude} ${thisInclude}`;
const includePath = `${napiInclude} ${thisInclude}`;
const binPath = currentDir;
const mkdirPath = isWindows ? `${rootPath}/bat/mkdir.bat` : 'mkdir';
@ -65,21 +50,11 @@ module.exports = {
paths,
binPath,
rootPath,
includePath,
mkdirPath,
rmPath,
cpPath,
platform: platformName,
include: includePath,
bin() { return console.log(binPath); },
root() { return console.log(rootPath); },
include() { console.log(includePath); },
mkdir() { return console.log(mkdirPath); },
rm() { return console.log(rmPath); },
cp() { return console.log(cpPath); },
download,
mkdir: mkdirPath,
rm: rmPath,
cp: cpPath,
};

36
install.js Normal file
View File

@ -0,0 +1,36 @@
'use strict';
const https = require('https');
const unzipper = require('unzipper');
const unzipper = require('unzipper');
const REPO = process.argv[1];
const download = (url, count = 1) => {
const sendReq = https.get(url, response => {
if ([301, 302, 303, 307].includes(response.statusCode)) {
if (count < 5) {
return download(response.headers.location, count + 1);
}
console.error('Error: Too many redirects.');
process.exit(-1);
return;
}
if (response.statusCode !== 200) {
console.log('Response status was ' + response.statusCode);
process.exit(-1);
return;
}
response.pipe(unzipper.Extract({ path: 'bin' }));
});
sendReq.on('error', err => {
console.log(err.message);
process.exit(-1);
});
};
download('https://github.com/raub/test-download/releases/download/v1.0.0/win.zip');

View File

@ -1,30 +0,0 @@
'use strict';
const https = require('https');
const http = require('http');
const WritableBuffer = require('./writable-buffer');
const protocols = { http, https };
module.exports = url => new Promise(
(res, rej) => {
url = url.toLowerCase();
const stream = new WritableBuffer();
const proto = protocols[url.match(/^https?/)[0]];
proto.get(url, response => {
response.pipe(stream);
response.on('end', () => res(stream.get()));
response.on('error', err => rej(err));
});
}
);

View File

@ -1,39 +1,32 @@
{
"author": "Luis Blanco <luisblanco1337@gmail.com>",
"name": "addon-tools-raub",
"version": "4.2.0",
"version": "5.0.0",
"description": "Helpers for Node.js addons and dependency packages",
"license": "MIT",
"main": "index.js",
"keywords": [
"support",
"headers",
"include",
"eventemitter",
"events",
"utils",
"c++",
"addon",
"bindings",
"native",
"napi",
"gyp"
],
"engines": {
"node": ">=10.13.0",
"node": ">=10.16.1",
"npm": ">=6.4.1"
},
"maintainers": [
{
"name": "Luis Blanco",
"email": "luisblanco1337@gmail.com",
"skype": "rauber666"
}
],
"repository": {
"type": "git",
"url": "https://github.com/node-3d/addon-tools-raub.git"
},
"dependencies": {
"nan": "2.12.1"
"node-addon-api": "1.7.1",
"unzipper": "0.10.1"
}
}

View File

@ -5,12 +5,12 @@
"main": "mocha",
"scripts": {
"test": "mocha",
"start": "mocha"
"preinstall": "cd .. && npm i"
},
"dependencies": {
"addon-tools-raub": "https://github.com/node-3d/addon-tools-raub.git",
"chai": "^4.2.0",
"mocha": "^5.2.0",
"sinon": "^7.1.1"
"addon-tools-raub": "..",
"chai": "4.2.0",
"mocha": "6.2.0",
"sinon": "7.3.2"
}
}