diff --git a/.gitignore b/.gitignore index 535e588..89eb7b3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,11 +1,8 @@ -.idea .cproject -.project +.idea .lock-wscript -build*/ .DS_Store -Debug/ +.project node_modules/ package-lock.json -binary/ *.log diff --git a/.npmignore b/.npmignore index 8ebed11..899fcd7 100644 --- a/.npmignore +++ b/.npmignore @@ -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 diff --git a/.travis.yml b/.travis.yml index 1913e0b..5553916 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,7 @@ language: node_js node_js: - - "10.13.0" + - "10.16.1" matrix: diff --git a/README.md b/README.md index 6252415..72b0f6e 100644 --- a/README.md +++ b/README.md @@ -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 -
+### Crossplatform commands -Crossplatform commands - - ``` - 'variables': { - 'rm' : ' +``` +'variables': { + 'rm' : ' +### Addon binary directory -Addon binary directory - - ``` - 'variables': { - 'binary' : ' +``` +'variables': { + 'binary' : ' +### Include directories -Include directories - - ``` - 'include_dirs': [ - ' +``` + 'include_dirs': [ + ' - -Remove intermediates - - ``` - [ '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. - -
+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 diff --git a/download.js b/download.js new file mode 100644 index 0000000..6db717d --- /dev/null +++ b/download.js @@ -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)); + + }); + +}); diff --git a/examples/addon/.eslintrc b/examples/addon/.eslintrc deleted file mode 100644 index c3126e7..0000000 --- a/examples/addon/.eslintrc +++ /dev/null @@ -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] - } -} diff --git a/examples/addon/.gitignore b/examples/addon/.gitignore deleted file mode 100644 index 5e2be6a..0000000 --- a/examples/addon/.gitignore +++ /dev/null @@ -1,12 +0,0 @@ -.idea -.cproject -.project -.lock-wscript -build*/ -bin-*/ -.DS_Store -Debug/ -node_modules/ -package-lock.json -binary/ -*.log diff --git a/examples/addon/.npmignore b/examples/addon/.npmignore deleted file mode 100644 index 827bee8..0000000 --- a/examples/addon/.npmignore +++ /dev/null @@ -1,16 +0,0 @@ -*.log -.cproject -.eslintrc -.gitignore -.idea -.lock-wscript -.project -binary/ -bin-*/ -build*/ -CPPLINT.cfg -Debug/ -examples/ -package-lock.json -test/ -qt/ diff --git a/examples/addon/CPPLINT.cfg b/examples/addon/CPPLINT.cfg deleted file mode 100644 index 9becc1c..0000000 --- a/examples/addon/CPPLINT.cfg +++ /dev/null @@ -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 diff --git a/examples/addon/binding.gyp b/examples/addon/binding.gyp deleted file mode 100644 index 1f0863b..0000000 --- a/examples/addon/binding.gyp +++ /dev/null @@ -1,91 +0,0 @@ -{ - 'variables': { - 'rm' : ' - -#include - -#include "example.hpp" - - -extern "C" { - - -void init(V8_VAR_OBJ target) { - - EventEmitter::init(target); - - Example::init(target); - -} - - -NODE_MODULE(example, init); - - -} // extern "C" diff --git a/examples/addon/cpp/example.cpp b/examples/addon/cpp/example.cpp deleted file mode 100644 index fd046bb..0000000 --- a/examples/addon/cpp/example.cpp +++ /dev/null @@ -1,105 +0,0 @@ -#include - -#include "example.hpp" - - -using namespace v8; -using namespace node; -using namespace std; - - -// ------ Aux macros - -#define THIS_EXAMPLE \ - Example *example = ObjectWrap::Unwrap(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(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(); - -} diff --git a/examples/addon/cpp/example.hpp b/examples/addon/cpp/example.hpp deleted file mode 100644 index a250946..0000000 --- a/examples/addon/cpp/example.hpp +++ /dev/null @@ -1,39 +0,0 @@ -#ifndef _EXAMPLE_HPP_ -#define _EXAMPLE_HPP_ - - -#include - - -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_ diff --git a/examples/addon/index.js b/examples/addon/index.js deleted file mode 100644 index 04c0ba3..0000000 --- a/examples/addon/index.js +++ /dev/null @@ -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; diff --git a/examples/addon/package.json b/examples/addon/package.json deleted file mode 100644 index 0d3dd95..0000000 --- a/examples/addon/package.json +++ /dev/null @@ -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" - } -} diff --git a/examples/deps/.gitignore b/examples/deps/.gitignore deleted file mode 100644 index 744f3bd..0000000 --- a/examples/deps/.gitignore +++ /dev/null @@ -1,9 +0,0 @@ -node_modules -*.log -build* -.DS_Store -*.pro.user -*.exp -*.pdb -*.ilk -package-lock.json diff --git a/examples/deps/.npmignore b/examples/deps/.npmignore deleted file mode 100644 index d8b5772..0000000 --- a/examples/deps/.npmignore +++ /dev/null @@ -1,11 +0,0 @@ -node_modules -*.log -build* -.DS_Store -*.pro.user -*.exp -*.pdb -*.ilk -.gitignore -package-lock.json -test diff --git a/examples/deps/bin-linux64/.keep b/examples/deps/bin-linux64/.keep deleted file mode 100644 index e69de29..0000000 diff --git a/examples/deps/bin-mac64/.keep b/examples/deps/bin-mac64/.keep deleted file mode 100644 index e69de29..0000000 diff --git a/examples/deps/bin-win32/.keep b/examples/deps/bin-win32/.keep deleted file mode 100644 index e69de29..0000000 diff --git a/examples/deps/bin-win64/.keep b/examples/deps/bin-win64/.keep deleted file mode 100644 index e69de29..0000000 diff --git a/examples/deps/binding.gyp b/examples/deps/binding.gyp deleted file mode 100644 index 2170073..0000000 --- a/examples/deps/binding.gyp +++ /dev/null @@ -1,21 +0,0 @@ -{ - 'variables': { - 'rm' : ' +#include -#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(val)) +#define JS_EXT(val) Napi::External::New(env, reinterpret_cast(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_VAR_VAL; -typedef v8::Local V8_VAR_OBJ; -typedef v8::Local V8_VAR_ARR; -typedef v8::Local V8_VAR_ABV; -typedef v8::Local V8_VAR_STR; -typedef v8::Local V8_VAR_FUNC; -typedef v8::Local V8_VAR_EXT; -typedef v8::Local V8_VAR_FT; -typedef v8::Local V8_VAR_OT; - -typedef Nan::Persistent V8_STORE_FT; -typedef Nan::Persistent V8_STORE_FUNC; -typedef Nan::Persistent V8_STORE_OBJ; -typedef Nan::Persistent V8_STORE_VAL; - - -#define JS_STR(...) Nan::New(__VA_ARGS__).ToLocalChecked() -#define JS_UTF8(...) Nan::New(__VA_ARGS__).ToLocalChecked() -#define JS_INT(val) Nan::New(val) -#define JS_INT32(val) Nan::New(val) -#define JS_UINT32(val) Nan::New(val) -#define JS_NUM(val) Nan::New(val) -#define JS_OFFS(val) Nan::New(static_cast(val)) -#define JS_FLOAT(val) Nan::New(val) -#define JS_DOUBLE(val) Nan::New(val) -#define JS_EXT(val) Nan::New(reinterpret_cast(val)) -#define JS_BOOL(val) (val) ? Nan::True() : Nan::False() -#define JS_FUN(val) Nan::New(val) -#define JS_OBJ(val) Nan::New(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()->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()->Value(); - #define REQ_UINT32_ARG(I, VAR) \ - CHECK_REQ_ARG(I, IsUint32(), "uint32"); \ - unsigned int VAR = info[I].As()->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()->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()->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()->Value(); #define REQ_OFFS_ARG(I, VAR) \ - CHECK_REQ_ARG(I, IsNumber(), "number"); \ - size_t VAR = static_cast(info[I].As()->Value()); + CHECK_REQ_ARG(I, IsNumber(), "Number"); \ + size_t VAR = static_cast(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( \ - info[I].As()->Value() \ - ); +#define USE_OFFS_ARG(I, VAR, DEF) \ + CHECK_LET_ARG(I, IsNumber(), "Number"); \ + size_t VAR = IS_ARG_EMPTY(I) \ + ? (DEF) \ + : static_cast(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()->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()->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(info[I].As()->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( \ - info[I].As()->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(); -#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(); + +#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(); #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(); + +#define USE_OBJ_ARG(I, VAR, DEF) \ + CHECK_LET_ARG(I, IsObject(), "Object"); \ + Napi::Object VAR = IS_ARG_EMPTY(I) ? (DEF) : info[I].As(); + +#define LET_OBJ_ARG(I, VAR) USE_OBJ_ARG(I, VAR, info[I].As()) + + +#define REQ_OBJ_ARG(I, VAR) \ + CHECK_REQ_ARG(I, IsObject(), "Object"); \ + Napi::Object VAR = info[I].As(); + +#define USE_OBJ_ARG(I, VAR, DEF) \ + CHECK_LET_ARG(I, IsObject(), "Object"); \ + Napi::Object VAR = IS_ARG_EMPTY(I) ? (DEF) : info[I].As(); + +#define LET_OBJ_ARG(I, VAR) USE_OBJ_ARG(I, VAR, info[I].As()) #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(); -#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 VAR = info[I].As< Napi::Buffer >(); #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()->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()->Value(); + SETTER_CHECK(IsBoolean(), "Bool"); \ + bool v = value.ToBoolean().Value(); #define SETTER_UINT32_ARG \ - SETTER_CHECK(IsUint32(), "uint32"); \ - unsigned int v = value.As()->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(value.As()->Value()); + SETTER_CHECK(IsNumber(), "Number"); \ + size_t v = static_cast(value.ToNumber().DoubleValue()); #define SETTER_DOUBLE_ARG \ - SETTER_CHECK(IsNumber(), "number"); \ - double v = value.As()->Value(); + SETTER_CHECK(IsNumber(), "Number"); \ + double v = value.ToNumber().DoubleValue(); #define SETTER_FLOAT_ARG \ - SETTER_CHECK(IsNumber(), "number"); \ - float v = static_cast(value.As()->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(); #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() #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() #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(); + + +#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 -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(); if (num) { - *num = arr->ByteLength() / sizeof(Type); + *num = arr.ByteLength() / sizeof(Type); } - data = reinterpret_cast(arr->Buffer()->GetContents().Data()); + data = static_cast(arr.Data()); + + return data; + +} + +template +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 arr = obj.As< Napi::Buffer >(); + 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(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(env, obj); + } else if (obj.IsTypedArray()) { + pixels = getArrayData( + env, + obj.As().ArrayBuffer() + ); + } else if (obj.Has("data")) { + Napi::Object data = obj.Get("data").As(); + if (data.IsArrayBuffer()) { + pixels = getArrayData(env, data); + } else if (data.IsBuffer()) { + pixels = getBufferData(env, data); + } else if (data.IsTypedArray()) { + pixels = getArrayData( + env, + data.As().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 args; + for (int i = 0; i < argc; i++) { + args.push_back(napi_value(argv[i])); + } - v8::Local log = v8::Script::Compile( - Nan::GetCurrentContext(), code - ).ToLocalChecked()->Run( - Nan::GetCurrentContext() - ).ToLocalChecked(); - Nan::Callback logCb(Nan::To(log).ToLocalChecked()); - - Nan::AsyncResource async("consoleLog()"); - - logCb.Call(argc, argv, &async); + log.As().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(); + + std::vector 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); } diff --git a/include/event-emitter.hpp b/include/event-emitter.hpp deleted file mode 100644 index e5a2f1a..0000000 --- a/include/event-emitter.hpp +++ /dev/null @@ -1,723 +0,0 @@ -#ifndef _EVENT_EMITTER_ -#define _EVENT_EMITTER_ - - -#include - -#include -#include -#include - - -#define THIS_EVENT_EMITTER \ - EventEmitter *eventEmitter = ObjectWrap::Unwrap(info.This()); - -#define EVENT_EMITTER_THIS_CHECK \ - if (eventEmitter->_isDestroyed) return; - - -// This template class provides static-member initialization in-header -template -class StaticHolder { -protected: - static V8_STORE_FT _protoEventEmitter; - static V8_STORE_FUNC _ctorEventEmitter; -}; -template V8_STORE_FT StaticHolder::_protoEventEmitter; -template V8_STORE_FUNC StaticHolder::_ctorEventEmitter; - - -class EventEmitter : public StaticHolder, public Nan::ObjectWrap { - - typedef Nan::CopyablePersistentTraits::CopyablePersistent FN_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; - typedef FNMAP_TYPE::iterator FNMAP_IT_TYPE; - -public: - - // Public V8 init - static void init(V8_VAR_OBJ target) { - - V8_VAR_FT proto = Nan::New(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(obj); - REQ_UTF8_ARG(1, name); - - const VEC_TYPE &list = eventEmitter->_listeners[*name]; - - RET_INT(static_cast(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(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(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(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 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 &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 &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 &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 &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 decor = v8::Script::Compile( - Nan::GetCurrentContext(), code - ).ToLocalChecked()->Run( - Nan::GetCurrentContext() - ).ToLocalChecked(); - Nan::Callback decorCb(Nan::To(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 ¤t = 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 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::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(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_ diff --git a/index.js b/index.js index 803b62a..9ccb713 100644 --- a/index.js +++ b/index.js @@ -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, }; diff --git a/install.js b/install.js new file mode 100644 index 0000000..201029c --- /dev/null +++ b/install.js @@ -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'); diff --git a/js/download.js b/js/download.js deleted file mode 100644 index 80a2a9c..0000000 --- a/js/download.js +++ /dev/null @@ -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)); - - }); - - } -); diff --git a/package.json b/package.json index 8cb8da4..b326abd 100644 --- a/package.json +++ b/package.json @@ -1,39 +1,32 @@ { "author": "Luis Blanco ", "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" } } diff --git a/test/package.json b/test/package.json index 29068a2..d131e78 100644 --- a/test/package.json +++ b/test/package.json @@ -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" } } diff --git a/js/writable-buffer.js b/writable-buffer.js similarity index 100% rename from js/writable-buffer.js rename to writable-buffer.js