wip new arch
This commit is contained in:
parent
1fb87351a5
commit
95173cd89c
|
@ -1,11 +1,8 @@
|
|||
.idea
|
||||
.cproject
|
||||
.project
|
||||
.idea
|
||||
.lock-wscript
|
||||
build*/
|
||||
.DS_Store
|
||||
Debug/
|
||||
.project
|
||||
node_modules/
|
||||
package-lock.json
|
||||
binary/
|
||||
*.log
|
||||
|
|
15
.npmignore
15
.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
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
language: node_js
|
||||
|
||||
node_js:
|
||||
- "10.13.0"
|
||||
- "10.16.1"
|
||||
|
||||
|
||||
matrix:
|
||||
|
|
112
README.md
112
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
|
||||
|
||||
<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()")',
|
||||
},
|
||||
```
|
||||
|
||||
On both Windows and Unix those are the console commands for various
|
||||
file system operations. No need for GYP conditions, yay!
|
||||
|
||||
</details>
|
||||
```
|
||||
'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>
|
||||
### 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 -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>
|
||||
```
|
||||
'include_dirs': [
|
||||
'<!@(node -p "require(\'addon-tools-raub\').include")',
|
||||
],
|
||||
```
|
||||
|
||||
|
||||
<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
|
||||
|
|
|
@ -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));
|
||||
|
||||
});
|
||||
|
||||
});
|
|
@ -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]
|
||||
}
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
.idea
|
||||
.cproject
|
||||
.project
|
||||
.lock-wscript
|
||||
build*/
|
||||
bin-*/
|
||||
.DS_Store
|
||||
Debug/
|
||||
node_modules/
|
||||
package-lock.json
|
||||
binary/
|
||||
*.log
|
|
@ -1,16 +0,0 @@
|
|||
*.log
|
||||
.cproject
|
||||
.eslintrc
|
||||
.gitignore
|
||||
.idea
|
||||
.lock-wscript
|
||||
.project
|
||||
binary/
|
||||
bin-*/
|
||||
build*/
|
||||
CPPLINT.cfg
|
||||
Debug/
|
||||
examples/
|
||||
package-lock.json
|
||||
test/
|
||||
qt/
|
|
@ -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
|
|
@ -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/*.*'
|
||||
] } ],
|
||||
],
|
||||
}],
|
||||
},
|
||||
]
|
||||
}
|
|
@ -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;
|
|
@ -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"
|
|
@ -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();
|
||||
|
||||
}
|
|
@ -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_
|
|
@ -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;
|
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
node_modules
|
||||
*.log
|
||||
build*
|
||||
.DS_Store
|
||||
*.pro.user
|
||||
*.exp
|
||||
*.pdb
|
||||
*.ilk
|
||||
package-lock.json
|
|
@ -1,11 +0,0 @@
|
|||
node_modules
|
||||
*.log
|
||||
build*
|
||||
.DS_Store
|
||||
*.pro.user
|
||||
*.exp
|
||||
*.pdb
|
||||
*.ilk
|
||||
.gitignore
|
||||
package-lock.json
|
||||
test
|
|
@ -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)'],
|
||||
}
|
||||
]}]],
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
module.exports = require('addon-tools-raub').paths(__dirname);
|
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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 ¤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<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_
|
71
index.js
71
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,
|
||||
|
||||
};
|
||||
|
|
|
@ -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');
|
|
@ -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));
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
);
|
17
package.json
17
package.json
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue