231103.1 · 7f4a9ed91bbecb84e39f4bc8030247ce5116ddf8
Reorganize examples into categorized folders (#341)
* refactor: create directory structure to organize exmaples
* test: update test_all to work with new directory structure
* fix: bug in test_all if no package.json scripts
* test: cleanup output of test_all
* fix: remove default test script that throws from thread_safe_function_with_object_wrap
* fix: remove build_with_cmake from .gitignore
* fix: swith to ' from " and remove subdir from getAllTests call
* test: add extra test cases for npm start and just running the pkgJson.main with node
* refactor: move all examples to src directory
* refactor: add numbers to categories so they are ordered in the src folder
* refactor: simplify getAllExamples in test_all.js
* docs: add directory structure to README.md
* docs: update directory structure to README.md
* docs: update directory structure to README.md
* docs: update directory structure to README.md
* docs: update directory structure example in readme
* add back build_with_cmake
---------
Co-authored-by: legendecas <legendecas@gmail.com>
reading declarations
@@ -1,5 +1,4 @@ build/-build_with_cmake/ node_modules/ Debug/ Release/
1_hello_world/README.md
@@ -1,8 +0,0 @@-## Example 1: *Hello world*--To get started let's make a small addon which is the C++ equivalent of-the following JavaScript code:--```js-module.exports.hello = function() { return 'world'; };-```
1_hello_world/nan/binding.gyp
@@ -1,11 +0,0 @@-{- "targets": [- {- "target_name": "hello",- "sources": [ "hello.cc" ],- "include_dirs": [- "<!(node -e \"require('nan')\")"- ]- }- ]-}
1_hello_world/nan/hello.cc
@@ -1,17 +0,0 @@-#include <nan.h>--void Method(const Nan::FunctionCallbackInfo<v8::Value>& info) {- info.GetReturnValue().Set(Nan::New("world").ToLocalChecked());-}--void Init(v8::Local<v8::Object> exports) {- v8::Local<v8::Context> context =- exports->GetCreationContext().ToLocalChecked();- exports->Set(context,- Nan::New("hello").ToLocalChecked(),- Nan::New<v8::FunctionTemplate>(Method)- ->GetFunction(context)- .ToLocalChecked());-}--NODE_MODULE(hello, Init)
1_hello_world/nan/hello.js
@@ -1,3 +0,0 @@-var addon = require('bindings')('hello');--console.log(addon.hello()); // 'world'
1_hello_world/nan/package.json
@@ -1,15 +0,0 @@-{- "name": "hello_world",- "version": "0.0.0",- "description": "Node.js Addons Example #1",- "main": "hello.js",- "private": true,- "dependencies": {- "bindings": "~1.2.1",- "nan": "^2.0.0"- },- "scripts": {- "test": "node hello.js"- },- "gypfile": true-}
1_hello_world/napi/binding.gyp
@@ -1,8 +0,0 @@-{- "targets": [- {- "target_name": "hello",- "sources": [ "hello.c" ]- }- ]-}
1_hello_world/napi/hello.c
@@ -1,23 +0,0 @@-#include <assert.h>-#include <node_api.h>--static napi_value Method(napi_env env, napi_callback_info info) {- napi_status status;- napi_value world;- status = napi_create_string_utf8(env, "world", 5, &world);- assert(status == napi_ok);- return world;-}--#define DECLARE_NAPI_METHOD(name, func) \- { name, 0, func, 0, 0, 0, napi_default, 0 }--static napi_value Init(napi_env env, napi_value exports) {- napi_status status;- napi_property_descriptor desc = DECLARE_NAPI_METHOD("hello", Method);- status = napi_define_properties(env, exports, 1, &desc);- assert(status == napi_ok);- return exports;-}--NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)
1_hello_world/napi/hello.js
@@ -1,3 +0,0 @@-var addon = require('bindings')('hello');--console.log(addon.hello()); // 'world'
1_hello_world/napi/package.json
@@ -1,14 +0,0 @@-{- "name": "hello_world",- "version": "0.0.0",- "description": "Node.js Addons Example #1",- "main": "hello.js",- "private": true,- "dependencies": {- "bindings": "~1.2.1"- },- "scripts": {- "test": "node hello.js"- },- "gypfile": true-}
1_hello_world/node-addon-api-addon-class/binding.gyp
@@ -1,14 +0,0 @@-{- "targets": [- {- "target_name": "hello",- "cflags!": [ "-fno-exceptions" ],- "cflags_cc!": [ "-fno-exceptions" ],- "sources": [ "hello.cc" ],- "include_dirs": [- "<!@(node -p \"require('node-addon-api').include\")"- ],- 'defines': ['NAPI_DISABLE_CPP_EXCEPTIONS'],- }- ]-}
1_hello_world/node-addon-api-addon-class/hello.cc
@@ -1,17 +0,0 @@-#include <napi.h>--class HelloAddon : public Napi::Addon<HelloAddon> {- public:- HelloAddon(Napi::Env env, Napi::Object exports) {- DefineAddon(exports, {- InstanceMethod("hello", &HelloAddon::Hello, napi_enumerable)- });- }-- private:- Napi::Value Hello(const Napi::CallbackInfo& info) {- return Napi::String::New(info.Env(), "world");- }-};--NODE_API_ADDON(HelloAddon)
1_hello_world/node-addon-api-addon-class/hello.js
@@ -1,3 +0,0 @@-var addon = require('bindings')('hello');--console.log(addon.hello()); // 'world'
1_hello_world/node-addon-api-addon-class/package.json
@@ -1,15 +0,0 @@-{- "name": "hello_world",- "version": "0.0.0",- "description": "Node.js Addons Example #1",- "main": "hello.js",- "private": true,- "dependencies": {- "bindings": "~1.5.0",- "node-addon-api": "^7.0.0"- },- "scripts": {- "test": "node hello.js"- },- "gypfile": true-}
1_hello_world/node-addon-api/binding.gyp
@@ -1,14 +0,0 @@-{- "targets": [- {- "target_name": "hello",- "cflags!": [ "-fno-exceptions" ],- "cflags_cc!": [ "-fno-exceptions" ],- "sources": [ "hello.cc" ],- "include_dirs": [- "<!@(node -p \"require('node-addon-api').include\")"- ],- 'defines': [ 'NAPI_DISABLE_CPP_EXCEPTIONS' ],- }- ]-}
1_hello_world/node-addon-api/hello.cc
@@ -1,14 +0,0 @@-#include <napi.h>--Napi::String Method(const Napi::CallbackInfo& info) {- Napi::Env env = info.Env();- return Napi::String::New(env, "world");-}--Napi::Object Init(Napi::Env env, Napi::Object exports) {- exports.Set(Napi::String::New(env, "hello"),- Napi::Function::New(env, Method));- return exports;-}--NODE_API_MODULE(hello, Init)
1_hello_world/node-addon-api/hello.js
@@ -1,3 +0,0 @@-var addon = require('bindings')('hello');--console.log(addon.hello()); // 'world'
1_hello_world/node-addon-api/package.json
@@ -1,15 +0,0 @@-{- "name": "hello_world",- "version": "0.0.0",- "description": "Node.js Addons Example #1",- "main": "hello.js",- "private": true,- "dependencies": {- "bindings": "~1.2.1",- "node-addon-api": "^1.0.0"- },- "scripts": {- "test": "node hello.js"- },- "gypfile": true-}
2_function_arguments/README.md
@@ -1,2 +0,0 @@-## Example 2: *Function arguments*-
2_function_arguments/nan/addon.cc
@@ -1,33 +0,0 @@-#include <nan.h>--void Add(const Nan::FunctionCallbackInfo<v8::Value>& info) {- v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext();-- if (info.Length() < 2) {- Nan::ThrowTypeError("Wrong number of arguments");- return;- }-- if (!info[0]->IsNumber() || !info[1]->IsNumber()) {- Nan::ThrowTypeError("Wrong arguments");- return;- }-- double arg0 = info[0]->NumberValue(context).FromJust();- double arg1 = info[1]->NumberValue(context).FromJust();- v8::Local<v8::Number> num = Nan::New(arg0 + arg1);-- info.GetReturnValue().Set(num);-}--void Init(v8::Local<v8::Object> exports) {- v8::Local<v8::Context> context =- exports->GetCreationContext().ToLocalChecked();- exports->Set(context,- Nan::New("add").ToLocalChecked(),- Nan::New<v8::FunctionTemplate>(Add)- ->GetFunction(context)- .ToLocalChecked());-}--NODE_MODULE(addon, Init)
2_function_arguments/nan/addon.js
@@ -1,3 +0,0 @@-var addon = require('bindings')('addon.node')--console.log('This should be eight:', addon.add(3, 5))
2_function_arguments/nan/binding.gyp
@@ -1,11 +0,0 @@-{- "targets": [- {- "target_name": "addon",- "sources": [ "addon.cc" ],- "include_dirs": [- "<!(node -e \"require('nan')\")"- ]- }- ]-}
2_function_arguments/nan/package.json
@@ -1,15 +0,0 @@-{- "name": "function_arguments",- "version": "0.0.0",- "description": "Node.js Addons Example #2",- "main": "addon.js",- "private": true,- "dependencies": {- "bindings": "~1.5.0",- "nan": "^2.0.0"- },- "scripts": {- "test": "node addon.js"- },- "gypfile": true-}
2_function_arguments/napi/addon.c
@@ -1,56 +0,0 @@-#include <assert.h>-#include <node_api.h>-#include <stdio.h>-static napi_value Add(napi_env env, napi_callback_info info) {- napi_status status;-- size_t argc = 2;- napi_value args[2];- status = napi_get_cb_info(env, info, &argc, args, NULL, NULL);- assert(status == napi_ok);-- if (argc < 2) {- napi_throw_type_error(env, NULL, "Wrong number of arguments");- return NULL;- }-- napi_valuetype valuetype0;- status = napi_typeof(env, args[0], &valuetype0);- assert(status == napi_ok);-- napi_valuetype valuetype1;- status = napi_typeof(env, args[1], &valuetype1);- assert(status == napi_ok);-- if (valuetype0 != napi_number || valuetype1 != napi_number) {- napi_throw_type_error(env, NULL, "Wrong arguments");- return NULL;- }-- double value0;- status = napi_get_value_double(env, args[0], &value0);- assert(status == napi_ok);-- double value1;- status = napi_get_value_double(env, args[1], &value1);- assert(status == napi_ok);-- napi_value sum;- status = napi_create_double(env, value0 + value1, &sum);- assert(status == napi_ok);-- return sum;-}--#define DECLARE_NAPI_METHOD(name, func) \- { name, 0, func, 0, 0, 0, napi_default, 0 }--napi_value Init(napi_env env, napi_value exports) {- napi_status status;- napi_property_descriptor addDescriptor = DECLARE_NAPI_METHOD("add", Add);- status = napi_define_properties(env, exports, 1, &addDescriptor);- assert(status == napi_ok);- return exports;-}--NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)
2_function_arguments/napi/addon.js
@@ -1,3 +0,0 @@-var addon = require('bindings')('addon.node')--console.log('This should be eight:', addon.add(3, 5))
2_function_arguments/napi/binding.gyp
@@ -1,8 +0,0 @@-{- "targets": [- {- "target_name": "addon",- "sources": [ "addon.c" ]- }- ]-}
2_function_arguments/napi/package.json
@@ -1,14 +0,0 @@-{- "name": "function_arguments",- "version": "0.0.0",- "description": "Node.js Addons Example #2",- "main": "addon.js",- "private": true,- "dependencies": {- "bindings": "~1.2.1"- },- "scripts": {- "test": "node addon.js"- },- "gypfile": true-}
2_function_arguments/node-addon-api/addon.cc
@@ -1,29 +0,0 @@-#include <napi.h>--Napi::Value Add(const Napi::CallbackInfo& info) {- Napi::Env env = info.Env();-- if (info.Length() < 2) {- Napi::TypeError::New(env, "Wrong number of arguments")- .ThrowAsJavaScriptException();- return env.Null();- }-- if (!info[0].IsNumber() || !info[1].IsNumber()) {- Napi::TypeError::New(env, "Wrong arguments").ThrowAsJavaScriptException();- return env.Null();- }-- double arg0 = info[0].As<Napi::Number>().DoubleValue();- double arg1 = info[1].As<Napi::Number>().DoubleValue();- Napi::Number num = Napi::Number::New(env, arg0 + arg1);-- return num;-}--Napi::Object Init(Napi::Env env, Napi::Object exports) {- exports.Set(Napi::String::New(env, "add"), Napi::Function::New(env, Add));- return exports;-}--NODE_API_MODULE(addon, Init)
2_function_arguments/node-addon-api/addon.js
@@ -1,3 +0,0 @@-var addon = require('bindings')('addon.node')--console.log('This should be eight:', addon.add(3, 5))
2_function_arguments/node-addon-api/binding.gyp
@@ -1,14 +0,0 @@-{- "targets": [- {- "target_name": "addon",- "cflags!": [ "-fno-exceptions" ],- "cflags_cc!": [ "-fno-exceptions" ],- "sources": [ "addon.cc" ],- "include_dirs": [- "<!@(node -p \"require('node-addon-api').include\")"- ],- 'defines': [ 'NAPI_DISABLE_CPP_EXCEPTIONS' ],- }- ]-}
2_function_arguments/node-addon-api/package.json
@@ -1,15 +0,0 @@-{- "name": "function_arguments",- "version": "0.0.0",- "description": "Node.js Addons Example #2",- "main": "addon.js",- "private": true,- "dependencies": {- "bindings": "~1.5.0",- "node-addon-api": "^1.0.0"- },- "scripts": {- "test": "node addon.js"- },- "gypfile": true-}
3_callbacks/nan/addon.cc
@@ -1,15 +0,0 @@-#include <nan.h>--void RunCallback(const Nan::FunctionCallbackInfo<v8::Value>& info) {- v8::Local<v8::Function> cb = info[0].As<v8::Function>();- const unsigned argc = 1;- v8::Local<v8::Value> argv[argc] = {Nan::New("hello world").ToLocalChecked()};- Nan::AsyncResource resource("nan:makeCallback");- resource.runInAsyncScope(Nan::GetCurrentContext()->Global(), cb, argc, argv);-}--void Init(v8::Local<v8::Object> exports, v8::Local<v8::Object> module) {- Nan::SetMethod(module, "exports", RunCallback);-}--NODE_MODULE(addon, Init)
3_callbacks/nan/addon.js
@@ -1,5 +0,0 @@-var addon = require('bindings')('addon');--addon(function(msg){- console.log(msg); // 'hello world'-});
3_callbacks/nan/binding.gyp
@@ -1,11 +0,0 @@-{- "targets": [- {- "target_name": "addon",- "sources": [ "addon.cc" ],- "include_dirs": [- "<!(node -e \"require('nan')\")"- ]- }- ]-}
3_callbacks/nan/package.json
@@ -1,12 +0,0 @@-{- "name": "callbacks",- "version": "0.0.0",- "description": "Node.js Addons Example #3",- "main": "addon.js",- "private": true,- "gypfile": true,- "dependencies": {- "bindings": "~1.5.0",- "nan": "^2.14.0"- }-}
3_callbacks/napi/addon.c
@@ -1,37 +0,0 @@-#include <assert.h>-#include <node_api.h>--static napi_value RunCallback(napi_env env, const napi_callback_info info) {- napi_status status;-- size_t argc = 1;- napi_value args[1];- status = napi_get_cb_info(env, info, &argc, args, NULL, NULL);- assert(status == napi_ok);-- napi_value cb = args[0];-- napi_value argv[1];- status = napi_create_string_utf8(env, "hello world", NAPI_AUTO_LENGTH, argv);- assert(status == napi_ok);-- napi_value global;- status = napi_get_global(env, &global);- assert(status == napi_ok);-- napi_value result;- status = napi_call_function(env, global, cb, 1, argv, &result);- assert(status == napi_ok);-- return NULL;-}--static napi_value Init(napi_env env, napi_value exports) {- napi_value new_exports;- napi_status status = napi_create_function(- env, "", NAPI_AUTO_LENGTH, RunCallback, NULL, &new_exports);- assert(status == napi_ok);- return new_exports;-}--NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)
3_callbacks/napi/addon.js
@@ -1,5 +0,0 @@-var addon = require('bindings')('addon');--addon(function(msg){- console.log(msg); // 'hello world'-});
3_callbacks/napi/binding.gyp
@@ -1,8 +0,0 @@-{- "targets": [- {- "target_name": "addon",- "sources": [ "addon.c" ]- }- ]-}
3_callbacks/napi/package.json
@@ -1,11 +0,0 @@-{- "name": "napi_callbacks",- "version": "0.0.0",- "description": "Node.js Addons Example #3",- "main": "addon.js",- "private": true,- "gypfile": true,- "dependencies": {- "bindings": "~1.2.1"- }-}
3_callbacks/node-addon-api/addon.cc
@@ -1,13 +0,0 @@-#include <napi.h>--void RunCallback(const Napi::CallbackInfo& info) {- Napi::Env env = info.Env();- Napi::Function cb = info[0].As<Napi::Function>();- cb.Call(env.Global(), {Napi::String::New(env, "hello world")});-}--Napi::Object Init(Napi::Env env, Napi::Object exports) {- return Napi::Function::New(env, RunCallback);-}--NODE_API_MODULE(addon, Init)
3_callbacks/node-addon-api/addon.js
@@ -1,5 +0,0 @@-var addon = require('bindings')('addon');--addon(function(msg){- console.log(msg); // 'hello world'-});
3_callbacks/node-addon-api/binding.gyp
@@ -1,14 +0,0 @@-{- "targets": [- {- "target_name": "addon",- "cflags!": [ "-fno-exceptions" ],- "cflags_cc!": [ "-fno-exceptions" ],- "sources": [ "addon.cc" ],- "include_dirs": [- "<!@(node -p \"require('node-addon-api').include\")"- ],- 'defines': [ 'NAPI_DISABLE_CPP_EXCEPTIONS' ],- }- ]-}
3_callbacks/node-addon-api/package.json
@@ -1,12 +0,0 @@-{- "name": "callbacks",- "version": "0.0.0",- "description": "Node.js Addons Example #3",- "main": "addon.js",- "private": true,- "gypfile": true,- "dependencies": {- "bindings": "~1.5.0",- "node-addon-api": "^1.0.0"- }-}
4_object_factory/nan/addon.cc
@@ -1,23 +0,0 @@-#include <nan.h>--void CreateObject(const Nan::FunctionCallbackInfo<v8::Value>& info) {- v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext();- v8::Local<v8::Object> obj = Nan::New<v8::Object>();- obj->Set(context,- Nan::New("msg").ToLocalChecked(),- info[0]->ToString(context).ToLocalChecked());-- info.GetReturnValue().Set(obj);-}--void Init(v8::Local<v8::Object> exports, v8::Local<v8::Object> module) {- v8::Local<v8::Context> context =- exports->GetCreationContext().ToLocalChecked();- module->Set(context,- Nan::New("exports").ToLocalChecked(),- Nan::New<v8::FunctionTemplate>(CreateObject)- ->GetFunction(context)- .ToLocalChecked());-}--NODE_MODULE(addon, Init)
4_object_factory/nan/addon.js
@@ -1,5 +0,0 @@-var addon = require('bindings')('addon');--var obj1 = addon('hello');-var obj2 = addon('world');-console.log(obj1.msg+' '+obj2.msg); // 'hello world'
4_object_factory/nan/binding.gyp
@@ -1,11 +0,0 @@-{- "targets": [- {- "target_name": "addon",- "sources": [ "addon.cc" ],- "include_dirs": [- "<!(node -e \"require('nan')\")"- ]- }- ]-}
4_object_factory/nan/package.json
@@ -1,12 +0,0 @@-{- "name": "object_factory",- "version": "0.0.0",- "description": "Node.js Addons Example #4",- "main": "addon.js",- "private": true,- "gypfile": true,- "dependencies": {- "bindings": "~1.5.0",- "nan": "^2.14.0"- }-}
4_object_factory/napi/addon.c
@@ -1,30 +0,0 @@-#include <assert.h>-#include <node_api.h>--static napi_value CreateObject(napi_env env, const napi_callback_info info) {- napi_status status;-- size_t argc = 1;- napi_value args[1];- status = napi_get_cb_info(env, info, &argc, args, NULL, NULL);- assert(status == napi_ok);-- napi_value obj;- status = napi_create_object(env, &obj);- assert(status == napi_ok);-- status = napi_set_named_property(env, obj, "msg", args[0]);- assert(status == napi_ok);-- return obj;-}--static napi_value Init(napi_env env, napi_value exports) {- napi_value new_exports;- napi_status status = napi_create_function(- env, "", NAPI_AUTO_LENGTH, CreateObject, NULL, &new_exports);- assert(status == napi_ok);- return new_exports;-}--NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)
4_object_factory/napi/addon.js
@@ -1,5 +0,0 @@-var addon = require('bindings')('addon');--var obj1 = addon('hello');-var obj2 = addon('world');-console.log(obj1.msg+' '+obj2.msg); // 'hello world'
4_object_factory/napi/binding.gyp
@@ -1,8 +0,0 @@-{- "targets": [- {- "target_name": "addon",- "sources": [ "addon.c" ]- }- ]-}
4_object_factory/napi/package.json
@@ -1,11 +0,0 @@-{- "name": "object_factory",- "version": "0.0.0",- "description": "Node.js Addons Example #4",- "main": "addon.js",- "private": true,- "gypfile": true,- "dependencies": {- "bindings": "~1.2.1"- }-}
4_object_factory/node-addon-api/addon.cc
@@ -1,15 +0,0 @@-#include <napi.h>--Napi::Object CreateObject(const Napi::CallbackInfo& info) {- Napi::Env env = info.Env();- Napi::Object obj = Napi::Object::New(env);- obj.Set(Napi::String::New(env, "msg"), info[0].ToString());-- return obj;-}--Napi::Object Init(Napi::Env env, Napi::Object exports) {- return Napi::Function::New(env, CreateObject, "createObject");-}--NODE_API_MODULE(addon, Init)
4_object_factory/node-addon-api/addon.js
@@ -1,5 +0,0 @@-var addon = require('bindings')('addon');--var obj1 = addon('hello');-var obj2 = addon('world');-console.log(obj1.msg+' '+obj2.msg); // 'hello world'
4_object_factory/node-addon-api/binding.gyp
@@ -1,14 +0,0 @@-{- "targets": [- {- "target_name": "addon",- "cflags!": [ "-fno-exceptions" ],- "cflags_cc!": [ "-fno-exceptions" ],- "sources": [ "addon.cc" ],- "include_dirs": [- "<!@(node -p \"require('node-addon-api').include\")"- ],- 'defines': [ 'NAPI_DISABLE_CPP_EXCEPTIONS' ],- }- ]-}
4_object_factory/node-addon-api/package.json
@@ -1,12 +0,0 @@-{- "name": "object_factory",- "version": "0.0.0",- "description": "Node.js Addons Example #4",- "main": "addon.js",- "private": true,- "gypfile": true,- "dependencies": {- "bindings": "~1.2.1",- "node-addon-api": "^1.0.0"- }-}
5_function_factory/nan/addon.cc
@@ -1,23 +0,0 @@-#include <nan.h>--void MyFunction(const Nan::FunctionCallbackInfo<v8::Value>& info) {- info.GetReturnValue().Set(Nan::New("hello world").ToLocalChecked());-}--void CreateFunction(const Nan::FunctionCallbackInfo<v8::Value>& info) {- v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext();- v8::Local<v8::FunctionTemplate> tpl =- Nan::New<v8::FunctionTemplate>(MyFunction);- v8::Local<v8::Function> fn = tpl->GetFunction(context).ToLocalChecked();-- // omit this to make it anonymous- fn->SetName(Nan::New("theFunction").ToLocalChecked());-- info.GetReturnValue().Set(fn);-}--void Init(v8::Local<v8::Object> exports, v8::Local<v8::Object> module) {- Nan::SetMethod(module, "exports", CreateFunction);-}--NODE_MODULE(addon, Init)
5_function_factory/nan/addon.js
@@ -1,4 +0,0 @@-var addon = require('bindings')('addon');--var fn = addon();-console.log(fn()); // 'hello world'
5_function_factory/nan/binding.gyp
@@ -1,11 +0,0 @@-{- "targets": [- {- "target_name": "addon",- "sources": [ "addon.cc" ],- "include_dirs": [- "<!(node -e \"require('nan')\")"- ]- }- ]-}
5_function_factory/nan/package.json
@@ -1,12 +0,0 @@-{- "name": "function_factory",- "version": "0.0.0",- "description": "Node.js Addons Example #5",- "main": "addon.js",- "private": true,- "gypfile": true,- "dependencies": {- "bindings": "~1.5.0",- "nan": "^2.0.0"- }-}
5_function_factory/napi/addon.c
@@ -1,33 +0,0 @@-#include <node_api.h>-#include <assert.h>--static napi_value MyFunction(napi_env env, napi_callback_info info) {- napi_status status;-- napi_value str;- status = napi_create_string_utf8(env, "hello world", NAPI_AUTO_LENGTH, &str);- assert(status == napi_ok);-- return str;-}--static napi_value CreateFunction(napi_env env, napi_callback_info info) {- napi_status status;-- napi_value fn;- status = napi_create_function(- env, "theFunction", NAPI_AUTO_LENGTH, MyFunction, NULL, &fn);- assert(status == napi_ok);-- return fn;-}--static napi_value Init(napi_env env, napi_value exports) {- napi_value new_exports;- napi_status status = napi_create_function(- env, "", NAPI_AUTO_LENGTH, CreateFunction, NULL, &new_exports);- assert(status == napi_ok);- return new_exports;-}--NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)
5_function_factory/napi/addon.js
@@ -1,4 +0,0 @@-var addon = require('bindings')('addon');--var fn = addon();-console.log(fn()); // 'hello world'
5_function_factory/napi/binding.gyp
@@ -1,8 +0,0 @@-{- "targets": [- {- "target_name": "addon",- "sources": [ "addon.c" ]- }- ]-}
5_function_factory/napi/package.json
@@ -1,11 +0,0 @@-{- "name": "function_factory",- "version": "0.0.0",- "description": "Node.js Addons Example #5",- "main": "addon.js",- "private": true,- "gypfile": true,- "dependencies": {- "bindings": "~1.5.0"- }-}
5_function_factory/node-addon-api/addon.cc
@@ -1,18 +0,0 @@-#include <napi.h>--Napi::String MyFunction(const Napi::CallbackInfo& info) {- Napi::Env env = info.Env();- return Napi::String::New(env, "hello world");-}--Napi::Function CreateFunction(const Napi::CallbackInfo& info) {- Napi::Env env = info.Env();- Napi::Function fn = Napi::Function::New(env, MyFunction, "theFunction");- return fn;-}--Napi::Object Init(Napi::Env env, Napi::Object exports) {- return Napi::Function::New(env, CreateFunction, "createObject");-}--NODE_API_MODULE(addon, Init)
5_function_factory/node-addon-api/addon.js
@@ -1,4 +0,0 @@-var addon = require('bindings')('addon');--var fn = addon();-console.log(fn()); // 'hello world'
5_function_factory/node-addon-api/binding.gyp
@@ -1,14 +0,0 @@-{- "targets": [- {- "target_name": "addon",- "cflags!": [ "-fno-exceptions" ],- "cflags_cc!": [ "-fno-exceptions" ],- "sources": [ "addon.cc" ],- "include_dirs": [- "<!@(node -p \"require('node-addon-api').include\")"- ],- 'defines': [ 'NAPI_DISABLE_CPP_EXCEPTIONS' ],- }- ]-}
5_function_factory/node-addon-api/package.json
@@ -1,12 +0,0 @@-{- "name": "function_factory",- "version": "0.0.0",- "description": "Node.js Addons Example #5",- "main": "addon.js",- "private": true,- "gypfile": true,- "dependencies": {- "bindings": "~1.2.1",- "node-addon-api": "^7.0.0"- }-}
6_object_wrap/nan/addon.cc
@@ -1,8 +0,0 @@-#include <nan.h>-#include "myobject.h"--void InitAll(v8::Local<v8::Object> exports) {- MyObject::Init(exports);-}--NODE_MODULE(addon, InitAll)
6_object_wrap/nan/addon.js
@@ -1,13 +0,0 @@-var addon = require('bindings')('addon');--var obj = new addon.MyObject(10);-console.log( obj.plusOne() ); // 11-console.log( obj.plusOne() ); // 12-console.log( obj.plusOne() ); // 13--console.log( obj.multiply().value() ); // 13-console.log( obj.multiply(10).value() ); // 130--var newobj = obj.multiply(-1);-console.log( newobj.value() ); // -13-console.log( obj === newobj ); // false
6_object_wrap/nan/binding.gyp
@@ -1,11 +0,0 @@-{- "targets": [- {- "target_name": "addon",- "sources": [ "addon.cc", "myobject.cc" ],- "include_dirs": [- "<!(node -e \"require('nan')\")"- ]- }- ]-}
6_object_wrap/nan/myobject.cc
@@ -1,73 +0,0 @@-#include "myobject.h"--Nan::Persistent<v8::Function> MyObject::constructor;--MyObject::MyObject(double value) : value_(value) {}--MyObject::~MyObject() {}--void MyObject::Init(v8::Local<v8::Object> exports) {- v8::Local<v8::Context> context =- exports->GetCreationContext().ToLocalChecked();- Nan::HandleScope scope;-- // Prepare constructor template- v8::Local<v8::FunctionTemplate> tpl = Nan::New<v8::FunctionTemplate>(New);- tpl->SetClassName(Nan::New("MyObject").ToLocalChecked());- tpl->InstanceTemplate()->SetInternalFieldCount(1);-- // Prototype- Nan::SetPrototypeMethod(tpl, "value", GetValue);- Nan::SetPrototypeMethod(tpl, "plusOne", PlusOne);- Nan::SetPrototypeMethod(tpl, "multiply", Multiply);-- constructor.Reset(tpl->GetFunction(context).ToLocalChecked());- exports->Set(context,- Nan::New("MyObject").ToLocalChecked(),- tpl->GetFunction(context).ToLocalChecked());-}--void MyObject::New(const Nan::FunctionCallbackInfo<v8::Value>& info) {- v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext();- if (info.IsConstructCall()) {- // Invoked as constructor: `new MyObject(...)`- double value =- info[0]->IsUndefined() ? 0 : info[0]->NumberValue(context).FromJust();- MyObject* obj = new MyObject(value);- obj->Wrap(info.This());- info.GetReturnValue().Set(info.This());- } else {- // Invoked as plain function `MyObject(...)`, turn into construct call.- const int argc = 1;- v8::Local<v8::Value> argv[argc] = {info[0]};- v8::Local<v8::Function> cons = Nan::New<v8::Function>(constructor);- info.GetReturnValue().Set(- cons->NewInstance(context, argc, argv).ToLocalChecked());- }-}--void MyObject::GetValue(const Nan::FunctionCallbackInfo<v8::Value>& info) {- MyObject* obj = ObjectWrap::Unwrap<MyObject>(info.Holder());- info.GetReturnValue().Set(Nan::New(obj->value_));-}--void MyObject::PlusOne(const Nan::FunctionCallbackInfo<v8::Value>& info) {- MyObject* obj = ObjectWrap::Unwrap<MyObject>(info.Holder());- obj->value_ += 1;- info.GetReturnValue().Set(Nan::New(obj->value_));-}--void MyObject::Multiply(const Nan::FunctionCallbackInfo<v8::Value>& info) {- v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext();- MyObject* obj = ObjectWrap::Unwrap<MyObject>(info.Holder());- double multiple =- info[0]->IsUndefined() ? 1 : info[0]->NumberValue(context).FromJust();-- v8::Local<v8::Function> cons = Nan::New<v8::Function>(constructor);-- const int argc = 1;- v8::Local<v8::Value> argv[argc] = {Nan::New(obj->value_ * multiple)};-- info.GetReturnValue().Set(- cons->NewInstance(context, argc, argv).ToLocalChecked());-}
6_object_wrap/nan/myobject.h
@@ -1,22 +0,0 @@-#ifndef MYOBJECT_H-#define MYOBJECT_H--#include <nan.h>--class MyObject : public Nan::ObjectWrap {- public:- static void Init(v8::Local<v8::Object> exports);-- private:- explicit MyObject(double value = 0);- ~MyObject();-- static void New(const Nan::FunctionCallbackInfo<v8::Value>& info);- static void GetValue(const Nan::FunctionCallbackInfo<v8::Value>& info);- static void PlusOne(const Nan::FunctionCallbackInfo<v8::Value>& info);- static void Multiply(const Nan::FunctionCallbackInfo<v8::Value>& info);- static Nan::Persistent<v8::Function> constructor;- double value_;-};--#endif
6_object_wrap/nan/package.json
@@ -1,12 +0,0 @@-{- "name": "object_wrap",- "version": "0.0.0",- "description": "Node.js Addons Example #6",- "main": "addon.js",- "private": true,- "gypfile": true,- "dependencies": {- "bindings": "~1.5.0",- "nan": "^2.14.0"- }-}
6_object_wrap/napi/addon.cc
@@ -1,7 +0,0 @@-#include "myobject.h"--napi_value Init(napi_env env, napi_value exports) {- return MyObject::Init(env, exports);-}--NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)
6_object_wrap/napi/addon.js
@@ -1,13 +0,0 @@-var addon = require('bindings')('addon');--var obj = new addon.MyObject(10);-console.log( obj.plusOne() ); // 11-console.log( obj.plusOne() ); // 12-console.log( obj.plusOne() ); // 13--console.log( obj.multiply().value ); // 13-console.log( obj.multiply(10).value ); // 130--var newobj = obj.multiply(-1);-console.log( newobj.value ); // -13-console.log( obj === newobj ); // false
6_object_wrap/napi/binding.gyp
@@ -1,8 +0,0 @@-{- "targets": [- {- "target_name": "addon",- "sources": [ "addon.cc", "myobject.cc" ]- }- ]-}
6_object_wrap/napi/myobject.cc
@@ -1,223 +0,0 @@-#include "myobject.h"-#include <assert.h>--MyObject::MyObject(double value)- : value_(value), env_(nullptr), wrapper_(nullptr) {}--MyObject::~MyObject() {- napi_delete_reference(env_, wrapper_);-}--void MyObject::Destructor(napi_env env,- void* nativeObject,- void* /*finalize_hint*/) {- reinterpret_cast<MyObject*>(nativeObject)->~MyObject();-}--#define DECLARE_NAPI_METHOD(name, func) \- { name, 0, func, 0, 0, 0, napi_default, 0 }--napi_value MyObject::Init(napi_env env, napi_value exports) {- napi_status status;- napi_property_descriptor properties[] = {- {"value", 0, 0, GetValue, SetValue, 0, napi_default, 0},- DECLARE_NAPI_METHOD("plusOne", PlusOne),- DECLARE_NAPI_METHOD("multiply", Multiply),- };-- napi_value cons;- status = napi_define_class(- env, "MyObject", NAPI_AUTO_LENGTH, New, nullptr, 3, properties, &cons);- assert(status == napi_ok);-- // We will need the constructor `cons` later during the life cycle of the- // addon, so we store a persistent reference to it as the instance data for- // our addon. This will enable us to use `napi_get_instance_data` at any- // point during the life cycle of our addon to retrieve it. We cannot simply- // store it as a global static variable, because that will render our addon- // unable to support Node.js worker threads and multiple contexts on a single- // thread.- //- // The finalizer we pass as a lambda will be called when our addon is unloaded- // and is responsible for releasing the persistent reference and freeing the- // heap memory where we stored the persistent reference.- napi_ref* constructor = new napi_ref;- status = napi_create_reference(env, cons, 1, constructor);- assert(status == napi_ok);- status = napi_set_instance_data(- env,- constructor,- [](napi_env env, void* data, void* hint) {- napi_ref* constructor = static_cast<napi_ref*>(data);- napi_status status = napi_delete_reference(env, *constructor);- assert(status == napi_ok);- delete constructor;- },- nullptr);- assert(status == napi_ok);-- status = napi_set_named_property(env, exports, "MyObject", cons);- assert(status == napi_ok);- return exports;-}--napi_value MyObject::Constructor(napi_env env) {- void* instance_data = nullptr;- napi_status status = napi_get_instance_data(env, &instance_data);- assert(status == napi_ok);- napi_ref* constructor = static_cast<napi_ref*>(instance_data);-- napi_value cons;- status = napi_get_reference_value(env, *constructor, &cons);- assert(status == napi_ok);- return cons;-}--napi_value MyObject::New(napi_env env, napi_callback_info info) {- napi_status status;-- napi_value target;- status = napi_get_new_target(env, info, &target);- assert(status == napi_ok);- bool is_constructor = target != nullptr;-- if (is_constructor) {- // Invoked as constructor: `new MyObject(...)`- size_t argc = 1;- napi_value args[1];- napi_value jsthis;- status = napi_get_cb_info(env, info, &argc, args, &jsthis, nullptr);- assert(status == napi_ok);-- double value = 0;-- napi_valuetype valuetype;- status = napi_typeof(env, args[0], &valuetype);- assert(status == napi_ok);-- if (valuetype != napi_undefined) {- status = napi_get_value_double(env, args[0], &value);- assert(status == napi_ok);- }-- MyObject* obj = new MyObject(value);-- obj->env_ = env;- status = napi_wrap(env,- jsthis,- reinterpret_cast<void*>(obj),- MyObject::Destructor,- nullptr, // finalize_hint- &obj->wrapper_);- assert(status == napi_ok);-- return jsthis;- } else {- // Invoked as plain function `MyObject(...)`, turn into construct call.- size_t argc_ = 1;- napi_value args[1];- status = napi_get_cb_info(env, info, &argc_, args, nullptr, nullptr);- assert(status == napi_ok);-- const size_t argc = 1;- napi_value argv[argc] = {args[0]};-- napi_value instance;- status = napi_new_instance(env, Constructor(env), argc, argv, &instance);- assert(status == napi_ok);-- return instance;- }-}--napi_value MyObject::GetValue(napi_env env, napi_callback_info info) {- napi_status status;-- napi_value jsthis;- status = napi_get_cb_info(env, info, nullptr, nullptr, &jsthis, nullptr);- assert(status == napi_ok);-- MyObject* obj;- status = napi_unwrap(env, jsthis, reinterpret_cast<void**>(&obj));- assert(status == napi_ok);-- napi_value num;- status = napi_create_double(env, obj->value_, &num);- assert(status == napi_ok);-- return num;-}--napi_value MyObject::SetValue(napi_env env, napi_callback_info info) {- napi_status status;-- size_t argc = 1;- napi_value value;- napi_value jsthis;- status = napi_get_cb_info(env, info, &argc, &value, &jsthis, nullptr);- assert(status == napi_ok);-- MyObject* obj;- status = napi_unwrap(env, jsthis, reinterpret_cast<void**>(&obj));- assert(status == napi_ok);-- status = napi_get_value_double(env, value, &obj->value_);- assert(status == napi_ok);-- return nullptr;-}--napi_value MyObject::PlusOne(napi_env env, napi_callback_info info) {- napi_status status;-- napi_value jsthis;- status = napi_get_cb_info(env, info, nullptr, nullptr, &jsthis, nullptr);- assert(status == napi_ok);-- MyObject* obj;- status = napi_unwrap(env, jsthis, reinterpret_cast<void**>(&obj));- assert(status == napi_ok);-- obj->value_ += 1;-- napi_value num;- status = napi_create_double(env, obj->value_, &num);- assert(status == napi_ok);-- return num;-}--napi_value MyObject::Multiply(napi_env env, napi_callback_info info) {- napi_status status;-- size_t argc = 1;- napi_value args[1];- napi_value jsthis;- status = napi_get_cb_info(env, info, &argc, args, &jsthis, nullptr);- assert(status == napi_ok);-- napi_valuetype valuetype;- status = napi_typeof(env, args[0], &valuetype);- assert(status == napi_ok);-- double multiple = 1;- if (valuetype != napi_undefined) {- status = napi_get_value_double(env, args[0], &multiple);- assert(status == napi_ok);- }-- MyObject* obj;- status = napi_unwrap(env, jsthis, reinterpret_cast<void**>(&obj));- assert(status == napi_ok);-- const int kArgCount = 1;- napi_value argv[kArgCount];- status = napi_create_double(env, obj->value_ * multiple, argv);- assert(status == napi_ok);-- napi_value instance;- status = napi_new_instance(env, Constructor(env), kArgCount, argv, &instance);- assert(status == napi_ok);-- return instance;-}
6_object_wrap/napi/myobject.h
@@ -1,27 +0,0 @@-#ifndef TEST_ADDONS_NAPI_6_OBJECT_WRAP_MYOBJECT_H_-#define TEST_ADDONS_NAPI_6_OBJECT_WRAP_MYOBJECT_H_--#include <node_api.h>--class MyObject {- public:- static napi_value Init(napi_env env, napi_value exports);- static void Destructor(napi_env env, void* nativeObject, void* finalize_hint);-- private:- explicit MyObject(double value_ = 0);- ~MyObject();-- static napi_value New(napi_env env, napi_callback_info info);- static napi_value GetValue(napi_env env, napi_callback_info info);- static napi_value SetValue(napi_env env, napi_callback_info info);- static napi_value PlusOne(napi_env env, napi_callback_info info);- static napi_value Multiply(napi_env env, napi_callback_info info);- static inline napi_value Constructor(napi_env env);-- double value_;- napi_env env_;- napi_ref wrapper_;-};--#endif // TEST_ADDONS_NAPI_6_OBJECT_WRAP_MYOBJECT_H_
6_object_wrap/napi/package.json
@@ -1,11 +0,0 @@-{- "name": "object_wrap",- "version": "0.0.0",- "description": "Node.js Addons Example #6",- "main": "addon.js",- "private": true,- "gypfile": true,- "dependencies": {- "bindings": "~1.5.0"- }-}
6_object_wrap/node-addon-api/addon.cc
@@ -1,8 +0,0 @@-#include <napi.h>-#include "myobject.h"--Napi::Object InitAll(Napi::Env env, Napi::Object exports) {- return MyObject::Init(env, exports);-}--NODE_API_MODULE(addon, InitAll)
6_object_wrap/node-addon-api/addon.js
@@ -1,13 +0,0 @@-var addon = require('bindings')('addon');--var obj = new addon.MyObject(10);-console.log( obj.plusOne() ); // 11-console.log( obj.plusOne() ); // 12-console.log( obj.plusOne() ); // 13--console.log( obj.multiply().value() ); // 13-console.log( obj.multiply(10).value() ); // 130--var newobj = obj.multiply(-1);-console.log( newobj.value() ); // -13-console.log( obj === newobj ); // false
6_object_wrap/node-addon-api/binding.gyp
@@ -1,14 +0,0 @@-{- "targets": [- {- "target_name": "addon",- "cflags!": [ "-fno-exceptions" ],- "cflags_cc!": [ "-fno-exceptions" ],- "sources": [ "addon.cc", "myobject.cc" ],- "include_dirs": [- "<!@(node -p \"require('node-addon-api').include\")"- ],- 'defines': [ 'NAPI_DISABLE_CPP_EXCEPTIONS' ],- }- ]-}
6_object_wrap/node-addon-api/myobject.cc
@@ -1,58 +0,0 @@-#include "myobject.h"--Napi::Object MyObject::Init(Napi::Env env, Napi::Object exports) {- Napi::Function func =- DefineClass(env,- "MyObject",- {InstanceMethod("plusOne", &MyObject::PlusOne),- InstanceMethod("value", &MyObject::GetValue),- InstanceMethod("multiply", &MyObject::Multiply)});-- Napi::FunctionReference* constructor = new Napi::FunctionReference();- *constructor = Napi::Persistent(func);- env.SetInstanceData(constructor);-- exports.Set("MyObject", func);- return exports;-}--MyObject::MyObject(const Napi::CallbackInfo& info)- : Napi::ObjectWrap<MyObject>(info) {- Napi::Env env = info.Env();-- int length = info.Length();-- if (length <= 0 || !info[0].IsNumber()) {- Napi::TypeError::New(env, "Number expected").ThrowAsJavaScriptException();- return;- }-- Napi::Number value = info[0].As<Napi::Number>();- this->value_ = value.DoubleValue();-}--Napi::Value MyObject::GetValue(const Napi::CallbackInfo& info) {- double num = this->value_;-- return Napi::Number::New(info.Env(), num);-}--Napi::Value MyObject::PlusOne(const Napi::CallbackInfo& info) {- this->value_ = this->value_ + 1;-- return MyObject::GetValue(info);-}--Napi::Value MyObject::Multiply(const Napi::CallbackInfo& info) {- Napi::Number multiple;- if (info.Length() <= 0 || !info[0].IsNumber()) {- multiple = Napi::Number::New(info.Env(), 1);- } else {- multiple = info[0].As<Napi::Number>();- }-- Napi::Object obj = info.Env().GetInstanceData<Napi::FunctionReference>()->New(- {Napi::Number::New(info.Env(), this->value_ * multiple.DoubleValue())});-- return obj;-}
6_object_wrap/node-addon-api/myobject.h
@@ -1,19 +0,0 @@-#ifndef MYOBJECT_H-#define MYOBJECT_H--#include <napi.h>--class MyObject : public Napi::ObjectWrap<MyObject> {- public:- static Napi::Object Init(Napi::Env env, Napi::Object exports);- MyObject(const Napi::CallbackInfo& info);-- private:- Napi::Value GetValue(const Napi::CallbackInfo& info);- Napi::Value PlusOne(const Napi::CallbackInfo& info);- Napi::Value Multiply(const Napi::CallbackInfo& info);-- double value_;-};--#endif
6_object_wrap/node-addon-api/package.json
@@ -1,15 +0,0 @@-{- "name": "object_wrap",- "version": "0.0.0",- "description": "Node.js Addons Example #6",- "main": "addon.js",- "private": true,- "gypfile": true,- "engines": {- "node": "~10 >=10.20 || >=12.17"- },- "dependencies": {- "bindings": "~1.5.0",- "node-addon-api": "^3.0.0"- }-}
7_factory_wrap/nan/addon.cc
@@ -1,23 +0,0 @@-#include <nan.h>-#include "myobject.h"--void CreateObject(const Nan::FunctionCallbackInfo<v8::Value>& info) {- info.GetReturnValue().Set(MyObject::NewInstance(info[0]));-}--void InitAll(v8::Local<v8::Object> exports, v8::Local<v8::Object> module) {- v8::Local<v8::Context> context =- exports->GetCreationContext().ToLocalChecked();-- Nan::HandleScope scope;-- MyObject::Init();-- module->Set(context,- Nan::New("exports").ToLocalChecked(),- Nan::New<v8::FunctionTemplate>(CreateObject)- ->GetFunction(context)- .ToLocalChecked());-}--NODE_MODULE(addon, InitAll)
7_factory_wrap/nan/addon.js
@@ -1,11 +0,0 @@-var createObject = require('bindings')('addon');--var obj = createObject(10);-console.log( obj.plusOne() ); // 11-console.log( obj.plusOne() ); // 12-console.log( obj.plusOne() ); // 13--var obj2 = createObject(20);-console.log( obj2.plusOne() ); // 21-console.log( obj2.plusOne() ); // 22-console.log( obj2.plusOne() ); // 23
7_factory_wrap/nan/binding.gyp
@@ -1,11 +0,0 @@-{- "targets": [- {- "target_name": "addon",- "sources": [ "addon.cc", "myobject.cc" ],- "include_dirs": [- "<!(node -e \"require('nan')\")"- ]- }- ]-}
7_factory_wrap/nan/myobject.cc
@@ -1,56 +0,0 @@-#include "myobject.h"-#include <nan.h>--using namespace v8;--MyObject::MyObject(){};-MyObject::~MyObject(){};--Nan::Persistent<v8::Function> MyObject::constructor;--void MyObject::Init() {- Nan::HandleScope scope;-- // Prepare constructor template- v8::Local<v8::FunctionTemplate> tpl = Nan::New<v8::FunctionTemplate>(New);- tpl->SetClassName(Nan::New("MyObject").ToLocalChecked());- tpl->InstanceTemplate()->SetInternalFieldCount(1);- // Prototype- tpl->PrototypeTemplate()->Set(Nan::New("plusOne").ToLocalChecked(),- Nan::New<v8::FunctionTemplate>(PlusOne));-- constructor.Reset(- tpl->GetFunction(Nan::GetCurrentContext()).ToLocalChecked());-}--void MyObject::New(const Nan::FunctionCallbackInfo<v8::Value>& info) {- v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext();-- MyObject* obj = new MyObject();- obj->counter_ =- info[0]->IsUndefined() ? 0 : info[0]->NumberValue(context).FromJust();- obj->Wrap(info.This());-- info.GetReturnValue().Set(info.This());-}--v8::Local<v8::Object> MyObject::NewInstance(v8::Local<v8::Value> arg) {- Nan::EscapableHandleScope scope;-- const unsigned argc = 1;- v8::Local<v8::Value> argv[argc] = {arg};- v8::Local<v8::Function> cons = Nan::New<v8::Function>(constructor);- v8::Local<v8::Context> context =- v8::Isolate::GetCurrent()->GetCurrentContext();- v8::Local<v8::Object> instance =- cons->NewInstance(context, argc, argv).ToLocalChecked();-- return scope.Escape(instance);-}--void MyObject::PlusOne(const Nan::FunctionCallbackInfo<v8::Value>& info) {- MyObject* obj = ObjectWrap::Unwrap<MyObject>(info.This());- obj->counter_ += 1;-- info.GetReturnValue().Set(Nan::New(obj->counter_));-}
7_factory_wrap/nan/myobject.h
@@ -1,21 +0,0 @@-#ifndef MYOBJECT_H-#define MYOBJECT_H--#include <nan.h>--class MyObject : public Nan::ObjectWrap {- public:- static void Init();- static v8::Local<v8::Object> NewInstance(v8::Local<v8::Value> arg);-- private:- MyObject();- ~MyObject();-- static Nan::Persistent<v8::Function> constructor;- static void New(const Nan::FunctionCallbackInfo<v8::Value>& info);- static void PlusOne(const Nan::FunctionCallbackInfo<v8::Value>& info);- double counter_;-};--#endif
7_factory_wrap/nan/package.json
@@ -1,12 +0,0 @@-{- "name": "factory_wrap",- "version": "0.0.0",- "description": "Node.js Addons Example #7",- "main": "addon.js",- "private": true,- "gypfile": true,- "dependencies": {- "bindings": "~1.2.1",- "nan": "^2.0.0"- }-}
7_factory_wrap/napi/addon.cc
@@ -1,30 +0,0 @@-#include <assert.h>-#include "myobject.h"--napi_value CreateObject(napi_env env, napi_callback_info info) {- napi_status status;-- size_t argc = 1;- napi_value args[1];- status = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);- assert(status == napi_ok);-- napi_value instance;- status = MyObject::NewInstance(env, args[0], &instance);- assert(status == napi_ok);-- return instance;-}--napi_value Init(napi_env env, napi_value exports) {- napi_status status = MyObject::Init(env);- assert(status == napi_ok);-- napi_value new_exports;- status = napi_create_function(- env, "", NAPI_AUTO_LENGTH, CreateObject, nullptr, &new_exports);- assert(status == napi_ok);- return new_exports;-}--NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)
7_factory_wrap/napi/addon.js
@@ -1,11 +0,0 @@-var createObject = require('bindings')('addon');--var obj = createObject(10);-console.log( obj.plusOne() ); // 11-console.log( obj.plusOne() ); // 12-console.log( obj.plusOne() ); // 13--var obj2 = createObject(20);-console.log( obj2.plusOne() ); // 21-console.log( obj2.plusOne() ); // 22-console.log( obj2.plusOne() ); // 23
7_factory_wrap/napi/binding.gyp
@@ -1,8 +0,0 @@-{- "targets": [- {- "target_name": "addon",- "sources": [ "addon.cc", "myobject.cc" ]- }- ]-}
7_factory_wrap/napi/myobject.cc
@@ -1,137 +0,0 @@-#include "myobject.h"-#include <assert.h>--MyObject::MyObject() : env_(nullptr), wrapper_(nullptr) {}--MyObject::~MyObject() {- napi_delete_reference(env_, wrapper_);-}--void MyObject::Destructor(napi_env env,- void* nativeObject,- void* /*finalize_hint*/) {- reinterpret_cast<MyObject*>(nativeObject)->~MyObject();-}--#define DECLARE_NAPI_METHOD(name, func) \- { name, 0, func, 0, 0, 0, napi_default, 0 }--napi_status MyObject::Init(napi_env env) {- napi_status status;- napi_property_descriptor properties[] = {- DECLARE_NAPI_METHOD("plusOne", PlusOne),- };-- napi_value cons;- status = napi_define_class(- env, "MyObject", NAPI_AUTO_LENGTH, New, nullptr, 1, properties, &cons);- if (status != napi_ok) return status;-- // We will need the constructor `cons` later during the life cycle of the- // addon, so we store a persistent reference to it as the instance data for- // our addon. This will enable us to use `napi_get_instance_data` at any- // point during the life cycle of our addon to retrieve it. We cannot simply- // store it as a global static variable, because that will render our addon- // unable to support Node.js worker threads and multiple contexts on a single- // thread.- //- // The finalizer we pass as a lambda will be called when our addon is unloaded- // and is responsible for releasing the persistent reference and freeing the- // heap memory where we stored the persistent reference.- napi_ref* constructor = new napi_ref;- status = napi_create_reference(env, cons, 1, constructor);- assert(status == napi_ok);- status = napi_set_instance_data(- env,- constructor,- [](napi_env env, void* data, void* hint) {- napi_ref* constructor = static_cast<napi_ref*>(data);- napi_status status = napi_delete_reference(env, *constructor);- assert(status == napi_ok);- delete constructor;- },- nullptr);- assert(status == napi_ok);-- return napi_ok;-}--napi_value MyObject::New(napi_env env, napi_callback_info info) {- napi_status status;-- size_t argc = 1;- napi_value args[1];- napi_value jsthis;- status = napi_get_cb_info(env, info, &argc, args, &jsthis, nullptr);- assert(status == napi_ok);-- napi_valuetype valuetype;- status = napi_typeof(env, args[0], &valuetype);- assert(status == napi_ok);-- MyObject* obj = new MyObject();-- if (valuetype == napi_undefined) {- obj->counter_ = 0;- } else {- status = napi_get_value_double(env, args[0], &obj->counter_);- assert(status == napi_ok);- }-- obj->env_ = env;- status = napi_wrap(env,- jsthis,- reinterpret_cast<void*>(obj),- MyObject::Destructor,- nullptr, /* finalize_hint */- &obj->wrapper_);- assert(status == napi_ok);-- return jsthis;-}--napi_value MyObject::Constructor(napi_env env) {- void* instance_data = nullptr;- napi_status status = napi_get_instance_data(env, &instance_data);- assert(status == napi_ok);- napi_ref* constructor = static_cast<napi_ref*>(instance_data);-- napi_value cons;- status = napi_get_reference_value(env, *constructor, &cons);- assert(status == napi_ok);- return cons;-}--napi_status MyObject::NewInstance(napi_env env,- napi_value arg,- napi_value* instance) {- napi_status status;-- const int argc = 1;- napi_value argv[argc] = {arg};-- status = napi_new_instance(env, Constructor(env), argc, argv, instance);- if (status != napi_ok) return status;-- return napi_ok;-}--napi_value MyObject::PlusOne(napi_env env, napi_callback_info info) {- napi_status status;-- napi_value jsthis;- status = napi_get_cb_info(env, info, nullptr, nullptr, &jsthis, nullptr);- assert(status == napi_ok);-- MyObject* obj;- status = napi_unwrap(env, jsthis, reinterpret_cast<void**>(&obj));- assert(status == napi_ok);-- obj->counter_ += 1;-- napi_value num;- status = napi_create_double(env, obj->counter_, &num);- assert(status == napi_ok);-- return num;-}
7_factory_wrap/napi/myobject.h
@@ -1,26 +0,0 @@-#ifndef TEST_ADDONS_NAPI_7_FACTORY_WRAP_MYOBJECT_H_-#define TEST_ADDONS_NAPI_7_FACTORY_WRAP_MYOBJECT_H_--#include <node_api.h>--class MyObject {- public:- static napi_status Init(napi_env env);- static void Destructor(napi_env env, void* nativeObject, void* finalize_hint);- static napi_status NewInstance(napi_env env,- napi_value arg,- napi_value* instance);-- private:- MyObject();- ~MyObject();-- static inline napi_value Constructor(napi_env env);- static napi_value New(napi_env env, napi_callback_info info);- static napi_value PlusOne(napi_env env, napi_callback_info info);- double counter_;- napi_env env_;- napi_ref wrapper_;-};--#endif // TEST_ADDONS_NAPI_7_FACTORY_WRAP_MYOBJECT_H_
7_factory_wrap/napi/package.json
@@ -1,11 +0,0 @@-{- "name": "factory_wrap",- "version": "0.0.0",- "description": "Node.js Addons Example #7",- "main": "addon.js",- "private": true,- "gypfile": true,- "dependencies": {- "bindings": "~1.5.0"- }-}
7_factory_wrap/node-addon-api/addon.cc
@@ -1,14 +0,0 @@-#include <napi.h>-#include "myobject.h"--Napi::Object CreateObject(const Napi::CallbackInfo& info) {- return MyObject::NewInstance(info.Env(), info[0]);-}--Napi::Object InitAll(Napi::Env env, Napi::Object exports) {- Napi::Object new_exports =- Napi::Function::New(env, CreateObject, "CreateObject");- return MyObject::Init(env, new_exports);-}--NODE_API_MODULE(addon, InitAll)
7_factory_wrap/node-addon-api/addon.js
@@ -1,11 +0,0 @@-var createObject = require('bindings')('addon');--var obj = createObject(10);-console.log( obj.plusOne() ); // 11-console.log( obj.plusOne() ); // 12-console.log( obj.plusOne() ); // 13--var obj2 = createObject(20);-console.log( obj2.plusOne() ); // 21-console.log( obj2.plusOne() ); // 22-console.log( obj2.plusOne() ); // 23
7_factory_wrap/node-addon-api/binding.gyp
@@ -1,14 +0,0 @@-{- "targets": [- {- "target_name": "addon",- "cflags!": [ "-fno-exceptions" ],- "cflags_cc!": [ "-fno-exceptions" ],- "sources": [ "addon.cc", "myobject.cc" ],- "include_dirs": [- "<!@(node -p \"require('node-addon-api').include\")"- ],- "defines": [ 'NAPI_DISABLE_CPP_EXCEPTIONS' ],- }- ]-}
7_factory_wrap/node-addon-api/myobject.cc
@@ -1,35 +0,0 @@-#include "myobject.h"-#include <napi.h>-#include <uv.h>--using namespace Napi;--Napi::Object MyObject::Init(Napi::Env env, Napi::Object exports) {- Napi::Function func = DefineClass(- env, "MyObject", {InstanceMethod("plusOne", &MyObject::PlusOne)});-- Napi::FunctionReference* constructor = new Napi::FunctionReference();- *constructor = Napi::Persistent(func);- env.SetInstanceData(constructor);-- exports.Set("MyObject", func);- return exports;-}--MyObject::MyObject(const Napi::CallbackInfo& info)- : Napi::ObjectWrap<MyObject>(info) {- this->counter_ = info[0].As<Napi::Number>().DoubleValue();-};--Napi::Object MyObject::NewInstance(Napi::Env env, Napi::Value arg) {- Napi::EscapableHandleScope scope(env);- Napi::Object obj = env.GetInstanceData<Napi::FunctionReference>()->New({arg});- return scope.Escape(napi_value(obj)).ToObject();-}--Napi::Value MyObject::PlusOne(const Napi::CallbackInfo& info) {- Napi::Env env = info.Env();- this->counter_ = this->counter_ + 1;-- return Napi::Number::New(env, this->counter_);-}
7_factory_wrap/node-addon-api/myobject.h
@@ -1,17 +0,0 @@-#ifndef MYOBJECT_H-#define MYOBJECT_H--#include <napi.h>--class MyObject : public Napi::ObjectWrap<MyObject> {- public:- static Napi::Object Init(Napi::Env env, Napi::Object exports);- static Napi::Object NewInstance(Napi::Env env, Napi::Value arg);- MyObject(const Napi::CallbackInfo& info);-- private:- Napi::Value PlusOne(const Napi::CallbackInfo& info);- double counter_;-};--#endif
7_factory_wrap/node-addon-api/package.json
@@ -1,15 +0,0 @@-{- "name": "factory_wrap",- "version": "0.0.0",- "description": "Node.js Addons Example #7",- "main": "addon.js",- "private": true,- "gypfile": true,- "engines": {- "node": "~10 >=10.20 || >=12.17"- },- "dependencies": {- "bindings": "~1.2.1",- "node-addon-api": "^7.0.0"- }-}
8_passing_wrapped/nan/addon.cc
@@ -1,40 +0,0 @@-#include <nan.h>-#include "myobject.h"--using namespace v8;--void CreateObject(const Nan::FunctionCallbackInfo<v8::Value>& info) {- info.GetReturnValue().Set(MyObject::NewInstance(info[0]));-}--void Add(const Nan::FunctionCallbackInfo<v8::Value>& info) {- v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext();-- MyObject* obj1 = Nan::ObjectWrap::Unwrap<MyObject>(- info[0]->ToObject(context).ToLocalChecked());- MyObject* obj2 = Nan::ObjectWrap::Unwrap<MyObject>(- info[1]->ToObject(context).ToLocalChecked());- double sum = obj1->Val() + obj2->Val();- info.GetReturnValue().Set(Nan::New(sum));-}--void InitAll(v8::Local<v8::Object> exports) {- v8::Local<v8::Context> context =- exports->GetCreationContext().ToLocalChecked();-- MyObject::Init();-- exports->Set(context,- Nan::New("createObject").ToLocalChecked(),- Nan::New<v8::FunctionTemplate>(CreateObject)- ->GetFunction(context)- .ToLocalChecked());-- exports->Set(context,- Nan::New("add").ToLocalChecked(),- Nan::New<v8::FunctionTemplate>(Add)- ->GetFunction(context)- .ToLocalChecked());-}--NODE_MODULE(addon, InitAll)
8_passing_wrapped/nan/addon.js
@@ -1,7 +0,0 @@-var addon = require('bindings')('addon');--var obj1 = addon.createObject(10);-var obj2 = addon.createObject(20);-var result = addon.add(obj1, obj2);--console.log(result); // 30
8_passing_wrapped/nan/binding.gyp
@@ -1,11 +0,0 @@-{- "targets": [- {- "target_name": "addon",- "sources": [ "addon.cc", "myobject.cc" ],- "include_dirs": [- "<!(node -e \"require('nan')\")"- ]- }- ]-}
8_passing_wrapped/nan/myobject.cc
@@ -1,43 +0,0 @@-#include "myobject.h"-#include <node.h>--MyObject::MyObject(){};-MyObject::~MyObject(){};--Nan::Persistent<v8::Function> MyObject::constructor;--void MyObject::Init() {- Nan::HandleScope scope;-- // Prepare constructor template- v8::Local<v8::FunctionTemplate> tpl = Nan::New<v8::FunctionTemplate>(New);- tpl->SetClassName(Nan::New("MyObject").ToLocalChecked());- tpl->InstanceTemplate()->SetInternalFieldCount(1);-- constructor.Reset(- tpl->GetFunction(Nan::GetCurrentContext()).ToLocalChecked());-}--void MyObject::New(const Nan::FunctionCallbackInfo<v8::Value>& info) {- v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext();- MyObject* obj = new MyObject();- obj->val_ =- info[0]->IsUndefined() ? 0 : info[0]->NumberValue(context).FromJust();- obj->Wrap(info.This());-- info.GetReturnValue().Set(info.This());-}--v8::Local<v8::Object> MyObject::NewInstance(v8::Local<v8::Value> arg) {- Nan::EscapableHandleScope scope;-- const unsigned argc = 1;- v8::Local<v8::Value> argv[argc] = {arg};- v8::Local<v8::Function> cons = Nan::New<v8::Function>(constructor);- v8::Local<v8::Context> context =- v8::Isolate::GetCurrent()->GetCurrentContext();- v8::Local<v8::Object> instance =- cons->NewInstance(context, argc, argv).ToLocalChecked();-- return scope.Escape(instance);-}
8_passing_wrapped/nan/myobject.h
@@ -1,21 +0,0 @@-#ifndef MYOBJECT_H-#define MYOBJECT_H--#include <nan.h>--class MyObject : public Nan::ObjectWrap {- public:- static void Init();- static v8::Local<v8::Object> NewInstance(v8::Local<v8::Value> arg);- double Val() const { return val_; }-- private:- MyObject();- ~MyObject();-- static Nan::Persistent<v8::Function> constructor;- static void New(const Nan::FunctionCallbackInfo<v8::Value>& info);- double val_;-};--#endif
8_passing_wrapped/nan/package.json
@@ -1,12 +0,0 @@-{- "name": "passing_wrapped",- "version": "0.0.0",- "description": "Node.js Addons Example #8",- "main": "addon.js",- "private": true,- "gypfile": true,- "dependencies": {- "bindings": "~1.5.0",- "nan": "^2.0.0"- }-}
8_passing_wrapped/napi/addon.cc
@@ -1,59 +0,0 @@-#include <assert.h>-#include "myobject.h"--napi_value CreateObject(napi_env env, napi_callback_info info) {- napi_status status;-- size_t argc = 1;- napi_value args[1];- status = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);- assert(status == napi_ok);-- napi_value instance;- status = MyObject::NewInstance(env, args[0], &instance);-- return instance;-}--napi_value Add(napi_env env, napi_callback_info info) {- napi_status status;-- size_t argc = 2;- napi_value args[2];- status = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);- assert(status == napi_ok);-- MyObject* obj1;- status = napi_unwrap(env, args[0], reinterpret_cast<void**>(&obj1));- assert(status == napi_ok);-- MyObject* obj2;- status = napi_unwrap(env, args[1], reinterpret_cast<void**>(&obj2));- assert(status == napi_ok);-- napi_value sum;- status = napi_create_double(env, obj1->Val() + obj2->Val(), &sum);- assert(status == napi_ok);-- return sum;-}--#define DECLARE_NAPI_METHOD(name, func) \- { name, 0, func, 0, 0, 0, napi_default, 0 }--napi_value Init(napi_env env, napi_value exports) {- napi_status status;-- MyObject::Init(env);-- napi_property_descriptor desc[] = {- DECLARE_NAPI_METHOD("createObject", CreateObject),- DECLARE_NAPI_METHOD("add", Add),- };- status =- napi_define_properties(env, exports, sizeof(desc) / sizeof(*desc), desc);- assert(status == napi_ok);- return exports;-}--NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)
8_passing_wrapped/napi/addon.js
@@ -1,7 +0,0 @@-var addon = require('bindings')('addon');--var obj1 = addon.createObject(10);-var obj2 = addon.createObject(20);-var result = addon.add(obj1, obj2);--console.log(result); // 30
8_passing_wrapped/napi/binding.gyp
@@ -1,8 +0,0 @@-{- "targets": [- {- "target_name": "addon",- "sources": [ "addon.cc", "myobject.cc" ]- }- ]-}
8_passing_wrapped/napi/myobject.cc
@@ -1,111 +0,0 @@-#include "myobject.h"-#include <assert.h>--MyObject::MyObject() : env_(nullptr), wrapper_(nullptr) {}--MyObject::~MyObject() {- napi_delete_reference(env_, wrapper_);-}--void MyObject::Destructor(napi_env env,- void* nativeObject,- void* /*finalize_hint*/) {- reinterpret_cast<MyObject*>(nativeObject)->~MyObject();-}--napi_status MyObject::Init(napi_env env) {- napi_status status;-- napi_value cons;- status = napi_define_class(- env, "MyObject", NAPI_AUTO_LENGTH, New, nullptr, 0, nullptr, &cons);- if (status != napi_ok) return status;-- // We will need the constructor `cons` later during the life cycle of the- // application, so we store a persistent reference to it as the instance data- // for our addon. This will enable us to use `napi_get_instance_data` at any- // point during the life cycle of our addon to retrieve it. We cannot simply- // store it as a global static variable, because that will render our addon- // unable to support Node.js worker threads and multiple contexts on a single- // thread.- //- // The finalizer we pass as a lambda will be called when our addon is unloaded- // and is responsible for releasing the persistent reference and freeing the- // heap memory where we stored the persistent reference.- napi_ref* constructor = new napi_ref;- status = napi_create_reference(env, cons, 1, constructor);- assert(status == napi_ok);- status = napi_set_instance_data(- env,- constructor,- [](napi_env env, void* data, void* hint) {- napi_ref* constructor = static_cast<napi_ref*>(data);- napi_status status = napi_delete_reference(env, *constructor);- assert(status == napi_ok);- delete constructor;- },- nullptr);- assert(status == napi_ok);-- return napi_ok;-}--napi_value MyObject::New(napi_env env, napi_callback_info info) {- napi_status status;-- size_t argc = 1;- napi_value args[1];- napi_value jsthis;- status = napi_get_cb_info(env, info, &argc, args, &jsthis, nullptr);- assert(status == napi_ok);-- MyObject* obj = new MyObject();-- napi_valuetype valuetype;- status = napi_typeof(env, args[0], &valuetype);- assert(status == napi_ok);-- if (valuetype == napi_undefined) {- obj->val_ = 0;- } else {- status = napi_get_value_double(env, args[0], &obj->val_);- assert(status == napi_ok);- }-- obj->env_ = env;- status = napi_wrap(env,- jsthis,- reinterpret_cast<void*>(obj),- MyObject::Destructor,- nullptr, // finalize_hint- &obj->wrapper_);- assert(status == napi_ok);-- return jsthis;-}--napi_value MyObject::Constructor(napi_env env) {- void* instance_data = nullptr;- napi_status status = napi_get_instance_data(env, &instance_data);- assert(status == napi_ok);- napi_ref* constructor = static_cast<napi_ref*>(instance_data);-- napi_value cons;- status = napi_get_reference_value(env, *constructor, &cons);- assert(status == napi_ok);- return cons;-}--napi_status MyObject::NewInstance(napi_env env,- napi_value arg,- napi_value* instance) {- napi_status status;-- const int argc = 1;- napi_value argv[argc] = {arg};-- status = napi_new_instance(env, Constructor(env), argc, argv, instance);- if (status != napi_ok) return status;-- return napi_ok;-}
8_passing_wrapped/napi/myobject.h
@@ -1,26 +0,0 @@-#ifndef TEST_ADDONS_NAPI_8_PASSING_WRAPPED_MYOBJECT_H_-#define TEST_ADDONS_NAPI_8_PASSING_WRAPPED_MYOBJECT_H_--#include <node_api.h>--class MyObject {- public:- static napi_status Init(napi_env env);- static void Destructor(napi_env env, void* nativeObject, void* finalize_hint);- static napi_status NewInstance(napi_env env,- napi_value arg,- napi_value* instance);- double Val() const { return val_; }-- private:- MyObject();- ~MyObject();-- static inline napi_value Constructor(napi_env env);- static napi_value New(napi_env env, napi_callback_info info);- double val_;- napi_env env_;- napi_ref wrapper_;-};--#endif // TEST_ADDONS_NAPI_8_PASSING_WRAPPED_MYOBJECT_H_
8_passing_wrapped/napi/package.json
@@ -1,11 +0,0 @@-{- "name": "passing_wrapped",- "version": "0.0.0",- "description": "Node.js Addons Example #8",- "main": "addon.js",- "private": true,- "gypfile": true,- "dependencies": {- "bindings": "~1.5.0"- }-}
8_passing_wrapped/node-addon-api/addon.cc
@@ -1,31 +0,0 @@-#include <napi.h>-#include "myobject.h"--using namespace Napi;--Napi::Object CreateObject(const Napi::CallbackInfo& info) {- return MyObject::NewInstance(info.Env(), info[0]);-}--Napi::Number Add(const Napi::CallbackInfo& info) {- Napi::Env env = info.Env();- MyObject* obj1 =- Napi::ObjectWrap<MyObject>::Unwrap(info[0].As<Napi::Object>());- MyObject* obj2 =- Napi::ObjectWrap<MyObject>::Unwrap(info[1].As<Napi::Object>());- double sum = obj1->Val() + obj2->Val();- return Napi::Number::New(env, sum);-}--Napi::Object InitAll(Napi::Env env, Napi::Object exports) {- MyObject::Init(env, exports);-- exports.Set(Napi::String::New(env, "createObject"),- Napi::Function::New(env, CreateObject));-- exports.Set(Napi::String::New(env, "add"), Napi::Function::New(env, Add));-- return exports;-}--NODE_API_MODULE(addon, InitAll)
8_passing_wrapped/node-addon-api/addon.js
@@ -1,7 +0,0 @@-var addon = require('bindings')('addon');--var obj1 = addon.createObject(10);-var obj2 = addon.createObject(20);-var result = addon.add(obj1, obj2);--console.log(result); // 30
8_passing_wrapped/node-addon-api/binding.gyp
@@ -1,16 +0,0 @@-{- "targets": [- {- "target_name": "addon",- "cflags!": [ "-fno-exceptions" ],- "cflags_cc!": [ "-fno-exceptions" ],- "cflags!": [ "-fno-exceptions" ],- "cflags_cc!": [ "-fno-exceptions" ],- "sources": [ "addon.cc", "myobject.cc" ],- "include_dirs": [- "<!@(node -p \"require('node-addon-api').include\")"- ],- 'defines': [ 'NAPI_DISABLE_CPP_EXCEPTIONS' ],- }- ]-}
8_passing_wrapped/node-addon-api/myobject.cc
@@ -1,24 +0,0 @@-#include "myobject.h"-#include <napi.h>-#include <uv.h>--MyObject::MyObject(const Napi::CallbackInfo& info)- : Napi::ObjectWrap<MyObject>(info) {- this->val_ = info[0].As<Napi::Number>().DoubleValue();-};--void MyObject::Init(Napi::Env env, Napi::Object exports) {- Napi::Function func = DefineClass(env, "MyObject", {});-- Napi::FunctionReference* constructor = new Napi::FunctionReference();- *constructor = Napi::Persistent(func);- env.SetInstanceData(constructor); //NOTE: this assumes only 1 class is exported- //for multiple exported classes, need a struct or other mechanism-- exports.Set("MyObject", func);-}--Napi::Object MyObject::NewInstance(Napi::Env env, Napi::Value arg) {- Napi::Object obj = env.GetInstanceData<Napi::FunctionReference>()->New({arg});- return obj;-}
8_passing_wrapped/node-addon-api/myobject.h
@@ -1,17 +0,0 @@-#ifndef MYOBJECT_H-#define MYOBJECT_H--#include <napi.h>--class MyObject : public Napi::ObjectWrap<MyObject> {- public:- static void Init(Napi::Env env, Napi::Object exports);- static Napi::Object NewInstance(Napi::Env env, Napi::Value arg);- double Val() const { return val_; }- MyObject(const Napi::CallbackInfo& info);-- private:- double val_;-};--#endif
8_passing_wrapped/node-addon-api/package.json
@@ -1,15 +0,0 @@-{- "name": "passing_wrapped",- "version": "0.0.0",- "description": "Node.js Addons Example #8",- "main": "addon.js",- "private": true,- "gypfile": true,- "engines": {- "node": "~10 >=10.20 || >=12.17"- },- "dependencies": {- "bindings": "~1.5.0",- "node-addon-api": "^3.0.0"- }-}
@@ -15,37 +15,45 @@ Implementations against unsupported versions of Node.js are provided for completeness and historical context. They are not maintained. The examples are primarily maintained for Node-API and node-addon-api and as outlined in-the Node.js [documentation](https://nodejs.org/dist/latest/docs/api/addons.html), +the Node.js [documentation](https://nodejs.org/dist/latest/docs/api/addons.html), unless there is a need for direct access to functionality which-is not exposed by Node-API, use Node-API. +is not exposed by Node-API, use Node-API. -The [Node-API Resource](http://nodejs.github.io/node-addon-examples/) offers an -excellent orientation and tips for developers just getting started with Node-API +The [Node-API Resource](http://nodejs.github.io/node-addon-examples/) offers an+excellent orientation and tips for developers just getting started with Node-API and `node-addon-api`. ## Usage The directory structure is as follows: +```sh+REPO_ROOT+├── test_all.js+├── package.json+├── README.md+└── src+ ├── 1-getting-started+ │ ├── example1+ │ │ ├── nan+ │ │ ├── node-addon-api+ │ │ └── napi+ │ ├── example2+ │ └── example3+ ├── 2-js-to-native-conversion+ ├── 3-context-awareness+ ├── 4-references-and-handle-scope+ ├── 5-async-work+ ├── 6-threadsafe-function+ ├── 7-events+ └── 8-tooling ```-<name of example>- |- +--- <implementation 1>- | |- | +--- files...- +--- <implementation 2>- . |- . +--- files...- .-```- In each example's implementation subdirectory, run ```text-$ npm install-$ node ./+npm install+node ./ ``` to see the example in action.-
a-first-project/node-addon-api/README.md
@@ -1,14 +0,0 @@-# Node-API A First Project--This is an example project that accompanies the Node-API workshop tutorials--A tutorial describing this project can be found at the [Node-API Resource](https://napi.inspiredware.com/getting-started/first.html).--To build and run this program on your system, clone it to your computer and run these two commands inside your clone:--```-npm install-npm test-```--> You need to have Node 10.5.0 or later installed.
a-first-project/node-addon-api/binding.gyp
@@ -1,20 +0,0 @@-{- 'targets': [- {- 'target_name': 'hello-world-native',- 'sources': [ 'src/hello_world.cc' ],- 'include_dirs': ["<!@(node -p \"require('node-addon-api').include\")"],- 'dependencies': ["<!(node -p \"require('node-addon-api').gyp\")"],- 'cflags!': [ '-fno-exceptions' ],- 'cflags_cc!': [ '-fno-exceptions' ],- 'xcode_settings': {- 'GCC_ENABLE_CPP_EXCEPTIONS': 'YES',- 'CLANG_CXX_LIBRARY': 'libc++',- 'MACOSX_DEPLOYMENT_TARGET': '10.7'- },- 'msvs_settings': {- 'VCCLCompilerTool': { 'ExceptionHandling': 1 },- }- }- ]-}
a-first-project/node-addon-api/lib/binding.js
@@ -1,4 +0,0 @@-const addon = require('../build/Release/hello-world-native');--module.exports = addon.HelloWorld-
a-first-project/node-addon-api/package.json
@@ -1,16 +0,0 @@-{- "main": "lib/binding.js",- "private": true,- "dependencies": {- "node-addon-api": "^1.1.0"- },- "scripts": {- "test": "node --napi-modules ./test/test_binding.js"- },- "gypfile": true,- "name": "hello-world",- "version": "1.0.0",- "description": "A first project.",- "author": "Your name goes here",- "license": "ISC"-}
a-first-project/node-addon-api/src/hello_world.cc
@@ -1,16 +0,0 @@-#include <napi.h>--using namespace Napi;--Napi::String Method(const Napi::CallbackInfo& info) {- Napi::Env env = info.Env();- return Napi::String::New(env, "world");-}--Napi::Object Init(Napi::Env env, Napi::Object exports) {- exports.Set(Napi::String::New(env, "HelloWorld"),- Napi::Function::New(env, Method));- return exports;-}--NODE_API_MODULE(addon, Init)
a-first-project/node-addon-api/test/test_binding.js
@@ -1,14 +0,0 @@-const HelloWorld = require("../lib/binding.js");-const assert = require("assert");--assert(HelloWorld, "The expected function is undefined");--function testBasic()-{- const result = HelloWorld("hello");- assert.strictEqual(result, "world", "Unexpected value returned");-}--assert.doesNotThrow(testBasic, undefined, "testBasic threw an expection");--console.log("Tests passed- everything looks OK!");
array_buffer_to_native/node-addon-api/array_buffer_to_native.cc
@@ -1,35 +0,0 @@-#include <napi.h>-#include <stdio.h>--static void ArrayConsumer(const int32_t* array, size_t length) {- for (size_t index = 0; index < length; index++) {- fprintf(stderr, "array[%lu] = %d\n", index, array[index]);- }-}--static Napi::Value AcceptArrayBuffer(const Napi::CallbackInfo& info) {- if (info.Length() != 1) {- Napi::Error::New(info.Env(), "Expected exactly one argument")- .ThrowAsJavaScriptException();- return info.Env().Undefined();- }- if (!info[0].IsArrayBuffer()) {- Napi::Error::New(info.Env(), "Expected an ArrayBuffer")- .ThrowAsJavaScriptException();- return info.Env().Undefined();- }-- Napi::ArrayBuffer buf = info[0].As<Napi::ArrayBuffer>();-- ArrayConsumer(reinterpret_cast<int32_t*>(buf.Data()),- buf.ByteLength() / sizeof(int32_t));-- return info.Env().Undefined();-}--static Napi::Object Init(Napi::Env env, Napi::Object exports) {- exports["AcceptArrayBuffer"] = Napi::Function::New(env, AcceptArrayBuffer);- return exports;-}--NODE_API_MODULE(NODE_GYP_MODULE_NAME, Init)
array_buffer_to_native/node-addon-api/binding.gyp
@@ -1,14 +0,0 @@-{- "targets": [- {- "target_name": "array_buffer_to_native",- "cflags!": [ "-fno-exceptions" ],- "cflags_cc!": [ "-fno-exceptions" ],- "sources": [ "array_buffer_to_native.cc" ],- "include_dirs": [- "<!@(node -p \"require('node-addon-api').include\")"- ],- 'defines': [ 'NAPI_DISABLE_CPP_EXCEPTIONS' ],- }- ]-}
array_buffer_to_native/node-addon-api/index.js
@@ -1,15 +0,0 @@-const binding = require('bindings')('array_buffer_to_native');-const array = new Int32Array(10);--array[0] = 19;-array[1] = -41;-array[2] = 98;-array[3] = -922;-array[4] = 587;-array[5] = 12;-array[6] = 221;-array[7] = 49;-array[8] = -96;-array[9] = -1;--binding.AcceptArrayBuffer(array.buffer);
array_buffer_to_native/node-addon-api/package.json
@@ -1,14 +0,0 @@-{- "name": "array_buffer_to_native",- "version": "0.0.0",- "description": "Fill Out a Native Array in JavaScript",- "main": "index.js",- "private": true,- "dependencies": {- "bindings": "~1.2.1",- "node-addon-api": "^1.0.0"- },- "scripts": {- "test": "node index.js"- }-}
async-iterator/node-addon-api/CMakeLists.txt
@@ -1,23 +0,0 @@-project (example)-include_directories(${CMAKE_JS_INC} node_modules/node-addon-api/)-cmake_minimum_required(VERSION 3.18)--set(CMAKE_CXX_STANDARD 20)-set(CMAKE_CXX_STANDARD_REQUIRED ON)-include_directories(${CMAKE_JS_INC})-file(GLOB SOURCE_FILES "*.cc")-add_library(${PROJECT_NAME} SHARED ${SOURCE_FILES} ${CMAKE_JS_SRC})-set_target_properties(${PROJECT_NAME} PROPERTIES PREFIX "" SUFFIX ".node")-target_link_libraries(${PROJECT_NAME} ${CMAKE_JS_LIB})--# Include Node-API wrappers-execute_process(COMMAND node -p "require('node-addon-api').include"- WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}- OUTPUT_VARIABLE NODE_ADDON_API_DIR- )-string(REGEX REPLACE "[\r\n\"]" "" NODE_ADDON_API_DIR ${NODE_ADDON_API_DIR})--target_include_directories(${PROJECT_NAME} PRIVATE ${NODE_ADDON_API_DIR})--# define NAPI_VERSION-add_definitions(-DNAPI_VERSION=6)
async-iterator/node-addon-api/example.cc
@@ -1,84 +0,0 @@-#include <napi.h>-#include <memory>--using namespace Napi;--class AsyncIteratorExample : public ObjectWrap<AsyncIteratorExample> {- public:- AsyncIteratorExample(const CallbackInfo& info)- : ObjectWrap<AsyncIteratorExample>(info),- _from(info[0].As<Number>()),- _to(info[1].As<Number>()) {}-- static Object Init(Napi::Env env, Napi::Object exports) {- Napi::Function func = DefineClass(- env,- "AsyncIteratorExample",- {InstanceMethod(Napi::Symbol::WellKnown(env, "asyncIterator"),- &AsyncIteratorExample::Iterator)});-- exports.Set("AsyncIteratorExample", func);- return exports;- }-- Napi::Value Iterator(const CallbackInfo& info) {- auto env = info.Env();- auto iteratorObject = Napi::Object::New(env);-- iteratorObject["current"] = Number::New(env, _from);- iteratorObject["last"] = Number::New(env, _to);- auto next = Function::New(env, [](const CallbackInfo& info) {- auto env = info.Env();- auto deferred =- std::make_shared<Promise::Deferred>(Promise::Deferred::New(env));- auto iteratorObject = info.This().As<Object>();- auto callback = Function::New(- env,- [=](const CallbackInfo& info) {- auto env = info.Env();- auto value = Object::New(env);- auto iteratorObject = info.This().As<Object>();-- auto current =- iteratorObject.Get("current").As<Number>().Int32Value();- auto last = iteratorObject.Get("last").As<Number>().Int32Value();- auto done = current > last;-- if (done) {- value["done"] = Boolean::New(env, true);- } else {- value["done"] = Boolean::New(env, false);- value["value"] = Number::New(env, current);- iteratorObject["current"] = Number::New(env, current + 1);- }- deferred->Resolve(value);- },- "next");-- env.Global()- .Get("setTimeout")- .As<Function>()- .Call({callback.Get("bind").As<Function>().Call(callback,- {iteratorObject}),- Number::New(env, 1000)});-- return deferred->Promise();- });-- iteratorObject["next"] =- next.Get("bind").As<Function>().Call(next, {iteratorObject});-- return iteratorObject;- }-- private:- int _from;- int _to;-};--Napi::Object Init(Napi::Env env, Object exports) {- AsyncIteratorExample::Init(env, exports);- return exports;-}--NODE_API_MODULE(example, Init)
async-iterator/node-addon-api/index.js
@@ -1,49 +0,0 @@-const { AsyncIteratorExample } = require('bindings')('example');--async function main(from, to) {- const iterator = new AsyncIteratorExample(from, to);- for await (const value of iterator) {- console.log(value);- }-}--/*-// The JavaScript equivalent of the node-addon-api C++ code for reference-async function main(from, to) {- class AsyncIteratorExample {- constructor(from, to) {- this.from = from;- this.to = to;- }-- [Symbol.asyncIterator]() {- return {- current: this.from,- last: this.to,- next() {- return new Promise(resolve => {- setTimeout(() => {- if (this.current <= this.last) {- resolve({ done: false, value: this.current++ });- } else {- resolve({ done: true });- }- }, 1000)- });- }- }- }- }- const iterator = new AsyncIteratorExample(from, to);-- for await (const value of iterator) {- console.log(value);- }-}-*/--main(0, 5)- .catch(e => {- console.error(e);- process.exit(1);- });
async-iterator/node-addon-api/package.json
@@ -1,16 +0,0 @@-{- "name": "async-iterator-example",- "version": "0.0.0",- "description": "Async iterator example using node-addon-api",- "main": "index.js",- "private": true,- "dependencies": {- "bindings": "^1.5.0",- "cmake-js": "^6.3.0",- "node-addon-api": "^5.0.0"- },- "scripts": {- "test": "node index.js",- "install": "cmake-js compile"- }-}
async_pi_estimate/nan/README.md
@@ -1 +0,0 @@-In this directory run `node-gyp rebuild` and then `node ./addon.js`
async_pi_estimate/nan/addon.cc
@@ -1,24 +0,0 @@-#include <nan.h>-#include "async.h" // NOLINT(build/include)-#include "sync.h" // NOLINT(build/include)--using Nan::GetFunction;-using Nan::New;-using Nan::Set;-using v8::FunctionTemplate;-using v8::Object;-using v8::String;--// Expose synchronous and asynchronous access to our-// Estimate() function-NAN_MODULE_INIT(InitAll) {- Set(target,- New<String>("calculateSync").ToLocalChecked(),- GetFunction(New<FunctionTemplate>(CalculateSync)).ToLocalChecked());-- Set(target,- New<String>("calculateAsync").ToLocalChecked(),- GetFunction(New<FunctionTemplate>(CalculateAsync)).ToLocalChecked());-}--NODE_MODULE(addon, InitAll)
async_pi_estimate/nan/addon.js
@@ -1,44 +0,0 @@-var addon = require('./build/Release/addon');-var calculations = process.argv[2] || 100000000;--function printResult(type, pi, ms) {- console.log(type, 'method:');- console.log('\tπ ≈ ' + pi +- ' (' + Math.abs(pi - Math.PI) + ' away from actual)');- console.log('\tTook ' + ms + 'ms');- console.log();-}--function runSync() {- var start = Date.now();- // Estimate() will execute in the current thread,- // the next line won't return until it is finished- var result = addon.calculateSync(calculations);- printResult('Sync', result, Date.now() - start);-}--function runAsync() {- // how many batches should we split the work in to?- var batches = process.argv[3] || 16;- var ended = 0;- var total = 0;- var start = Date.now();-- function done (err, result) {- total += result;-- // have all the batches finished executing?- if (++ended === batches) {- printResult('Async', total / batches, Date.now() - start);- }- }-- // for each batch of work, request an async Estimate() for- // a portion of the total number of calculations- for (var i = 0; i < batches; i++) {- addon.calculateAsync(calculations / batches, done);- }-}--runSync();-runAsync();
async_pi_estimate/nan/async.cc
@@ -1,51 +0,0 @@-#include "async.h" // NOLINT(build/include)-#include <nan.h>-#include "pi_est.h" // NOLINT(build/include)--using Nan::AsyncQueueWorker;-using Nan::AsyncWorker;-using Nan::Callback;-using Nan::HandleScope;-using Nan::New;-using Nan::Null;-using Nan::To;-using v8::Function;-using v8::Local;-using v8::Number;-using v8::Value;--class PiWorker : public AsyncWorker {- public:- PiWorker(Callback* callback, int points)- : AsyncWorker(callback), points(points), estimate(0) {}- ~PiWorker() {}-- // Executed inside the worker-thread.- // It is not safe to access V8, or V8 data structures- // here, so everything we need for input and output- // should go on `this`.- void Execute() { estimate = Estimate(points); }-- // Executed when the async work is complete- // this function will be run inside the main event loop- // so it is safe to use V8 again- void HandleOKCallback() {- HandleScope scope;-- Local<Value> argv[] = {Null(), New<Number>(estimate)};-- callback->Call(2, argv, async_resource);- }-- private:- int points;- double estimate;-};--// Asynchronous access to the `Estimate()` function-NAN_METHOD(CalculateAsync) {- int points = To<int>(info[0]).FromJust();- Callback* callback = new Callback(To<Function>(info[1]).ToLocalChecked());-- AsyncQueueWorker(new PiWorker(callback, points));-}
async_pi_estimate/nan/async.h
@@ -1,8 +0,0 @@-#ifndef EXAMPLES_ASYNC_PI_ESTIMATE_ASYNC_H_-#define EXAMPLES_ASYNC_PI_ESTIMATE_ASYNC_H_--#include <nan.h>--NAN_METHOD(CalculateAsync);--#endif // EXAMPLES_ASYNC_PI_ESTIMATE_ASYNC_H_
async_pi_estimate/nan/binding.gyp
@@ -1,14 +0,0 @@-{- "targets": [- {- "target_name": "addon",- "sources": [- "addon.cc",- "pi_est.cc",- "sync.cc",- "async.cc"- ],- "include_dirs": ["<!(node -e \"require('nan')\")"]- }- ]-}
async_pi_estimate/nan/package.json
@@ -1,14 +0,0 @@-{- "name": "async_work",- "version": "0.0.0",- "description": "Node.js Addons Example #9",- "main": "addon.js",- "private": true,- "gypfile": true,- "scripts": {- "test": "node addon.js"- },- "dependencies": {- "nan": "*"- }-}
async_pi_estimate/nan/pi_est.cc
@@ -1,54 +0,0 @@-#include "pi_est.h" // NOLINT(build/include)-#include <cstdlib>--/*-Estimate the value of π by using a Monte Carlo method.-Take `points` samples of random x and y values on a-[0,1][0,1] plane. Calculating the length of the diagonal-tells us whether the point lies inside, or outside a-quarter circle running from 0,1 to 1,0. The ratio of the-number of points inside to outside gives us an-approximation of π/4.--See https://en.wikipedia.org/wiki/File:Pi_30K.gif-for a visualization of how this works.-*/--inline int randall(unsigned int* p_seed) {-// windows has thread safe rand()-#ifdef _WIN32- return rand(); // NOLINT(runtime/threadsafe_fn)-#else- return rand_r(p_seed);-#endif-}--double Estimate(int points) {- int i = points;- int inside = 0;- unsigned int randseed = 1;--#ifdef _WIN32- srand(randseed);-#endif-- // unique seed for each run, for threaded use- unsigned int seed = randall(&randseed);--#ifdef _WIN32- srand(seed);-#endif-- while (i-- > 0) {- double x = randall(&seed) / static_cast<double>(RAND_MAX);- double y = randall(&seed) / static_cast<double>(RAND_MAX);-- // x & y and now values between 0 and 1- // now do a pythagorean diagonal calculation- // `1` represents our 1/4 circle- if ((x * x) + (y * y) <= 1) inside++;- }-- // calculate ratio and multiply by 4 for π- return (inside / static_cast<double>(points)) * 4;-}
async_pi_estimate/nan/pi_est.h
@@ -1,6 +0,0 @@-#ifndef EXAMPLES_ASYNC_PI_ESTIMATE_PI_EST_H_-#define EXAMPLES_ASYNC_PI_ESTIMATE_PI_EST_H_--double Estimate(int points);--#endif // EXAMPLES_ASYNC_PI_ESTIMATE_PI_EST_H_
async_pi_estimate/nan/sync.cc
@@ -1,12 +0,0 @@-#include "sync.h" // NOLINT(build/include)-#include <nan.h>-#include "pi_est.h" // NOLINT(build/include)--// Simple synchronous access to the `Estimate()` function-NAN_METHOD(CalculateSync) {- // expect a number as the first argument- int points = info[0]->Uint32Value(Nan::GetCurrentContext()).FromJust();- double est = Estimate(points);-- info.GetReturnValue().Set(est);-}
async_pi_estimate/nan/sync.h
@@ -1,8 +0,0 @@-#ifndef EXAMPLES_ASYNC_PI_ESTIMATE_SYNC_H_-#define EXAMPLES_ASYNC_PI_ESTIMATE_SYNC_H_--#include <nan.h>--NAN_METHOD(CalculateSync);--#endif // EXAMPLES_ASYNC_PI_ESTIMATE_SYNC_H_
async_pi_estimate/node-addon-api/README.md
@@ -1 +0,0 @@-In this directory run `node-gyp rebuild` and then `node ./addon.js`
async_pi_estimate/node-addon-api/addon.cc
@@ -1,15 +0,0 @@-#include <napi.h>-#include "async.h" // NOLINT(build/include)-#include "sync.h" // NOLINT(build/include)--// Expose synchronous and asynchronous access to our-// Estimate() function-Napi::Object Init(Napi::Env env, Napi::Object exports) {- exports.Set(Napi::String::New(env, "calculateSync"),- Napi::Function::New(env, CalculateSync));- exports.Set(Napi::String::New(env, "calculateAsync"),- Napi::Function::New(env, CalculateAsync));- return exports;-}--NODE_API_MODULE(NODE_GYP_MODULE_NAME, Init)
async_pi_estimate/node-addon-api/addon.js
@@ -1,44 +0,0 @@-var addon = require('bindings')('addon');-var calculations = process.argv[2] || 100000000;--function printResult(type, pi, ms) {- console.log(type, 'method:');- console.log('\tπ ≈ ' + pi +- ' (' + Math.abs(pi - Math.PI) + ' away from actual)');- console.log('\tTook ' + ms + 'ms');- console.log();-}--function runSync() {- var start = Date.now();- // Estimate() will execute in the current thread,- // the next line won't return until it is finished- var result = addon.calculateSync(calculations);- printResult('Sync', result, Date.now() - start);-}--function runAsync() {- // how many batches should we split the work in to?- var batches = process.argv[3] || 16;- var ended = 0;- var total = 0;- var start = Date.now();-- function done (err, result) {- total += result;-- // have all the batches finished executing?- if (++ended === batches) {- printResult('Async', total / batches, Date.now() - start);- }- }-- // for each batch of work, request an async Estimate() for- // a portion of the total number of calculations- for (var i = 0; i < batches; i++) {- addon.calculateAsync(calculations / batches, done);- }-}--runSync();-runAsync();
async_pi_estimate/node-addon-api/async.cc
@@ -1,36 +0,0 @@-#include "async.h" // NOLINT(build/include)-#include <napi.h>-#include "pi_est.h" // NOLINT(build/include)--class PiWorker : public Napi::AsyncWorker {- public:- PiWorker(Napi::Function& callback, int points)- : Napi::AsyncWorker(callback), points(points), estimate(0) {}- ~PiWorker() {}-- // Executed inside the worker-thread.- // It is not safe to access JS engine data structure- // here, so everything we need for input and output- // should go on `this`.- void Execute() { estimate = Estimate(points); }-- // Executed when the async work is complete- // this function will be run inside the main event loop- // so it is safe to use JS engine data again- void OnOK() {- Callback().Call({Env().Undefined(), Napi::Number::New(Env(), estimate)});- }-- private:- int points;- double estimate;-};--// Asynchronous access to the `Estimate()` function-Napi::Value CalculateAsync(const Napi::CallbackInfo& info) {- int points = info[0].As<Napi::Number>().Uint32Value();- Napi::Function callback = info[1].As<Napi::Function>();- PiWorker* piWorker = new PiWorker(callback, points);- piWorker->Queue();- return info.Env().Undefined();-}
async_pi_estimate/node-addon-api/async.h
@@ -1,8 +0,0 @@-#ifndef EXAMPLES_ASYNC_PI_ESTIMATE_ASYNC_H_-#define EXAMPLES_ASYNC_PI_ESTIMATE_ASYNC_H_--#include <napi.h>--Napi::Value CalculateAsync(const Napi::CallbackInfo& info);--#endif // EXAMPLES_ASYNC_PI_ESTIMATE_ASYNC_H_
async_pi_estimate/node-addon-api/binding.gyp
@@ -1,33 +0,0 @@-{- "targets": [- {- "target_name": "addon",- "sources": [- "addon.cc",- "pi_est.cc",- "sync.cc",- "async.cc"- ],- 'cflags!': [ '-fno-exceptions' ],- 'cflags_cc!': [ '-fno-exceptions' ],- 'include_dirs': ["<!@(node -p \"require('node-addon-api').include\")"],- 'dependencies': ["<!(node -p \"require('node-addon-api').gyp\")"],- 'conditions': [- ['OS=="win"', {- "msvs_settings": {- "VCCLCompilerTool": {- "ExceptionHandling": 1- }- }- }],- ['OS=="mac"', {- "xcode_settings": {- "CLANG_CXX_LIBRARY": "libc++",- 'GCC_ENABLE_CPP_EXCEPTIONS': 'YES',- 'MACOSX_DEPLOYMENT_TARGET': '10.7'- }- }]- ]- }- ]-}
async_pi_estimate/node-addon-api/package.json
@@ -1,15 +0,0 @@-{- "name": "async_work",- "version": "0.0.0",- "description": "Node.js Addons Example #9",- "main": "addon.js",- "private": true,- "gypfile": true,- "scripts": {- "test": "node addon.js"- },- "dependencies": {- "node-addon-api": "*",- "bindings": "*"- }-}
async_pi_estimate/node-addon-api/pi_est.cc
@@ -1,54 +0,0 @@-#include "pi_est.h" // NOLINT(build/include)-#include <cstdlib>--/*-Estimate the value of π by using a Monte Carlo method.-Take `points` samples of random x and y values on a-[0,1][0,1] plane. Calculating the length of the diagonal-tells us whether the point lies inside, or outside a-quarter circle running from 0,1 to 1,0. The ratio of the-number of points inside to outside gives us an-approximation of π/4.--See https://en.wikipedia.org/wiki/File:Pi_30K.gif-for a visualization of how this works.-*/--inline int randall(unsigned int* p_seed) {-// windows has thread safe rand()-#ifdef _WIN32- return rand(); // NOLINT(runtime/threadsafe_fn)-#else- return rand_r(p_seed);-#endif-}--double Estimate(int points) {- int i = points;- int inside = 0;- unsigned int randseed = 1;--#ifdef _WIN32- srand(randseed);-#endif-- // unique seed for each run, for threaded use- unsigned int seed = randall(&randseed);--#ifdef _WIN32- srand(seed);-#endif-- while (i-- > 0) {- double x = randall(&seed) / static_cast<double>(RAND_MAX);- double y = randall(&seed) / static_cast<double>(RAND_MAX);-- // x & y and now values between 0 and 1- // now do a pythagorean diagonal calculation- // `1` represents our 1/4 circle- if ((x * x) + (y * y) <= 1) inside++;- }-- // calculate ratio and multiply by 4 for π- return (inside / static_cast<double>(points)) * 4;-}
async_pi_estimate/node-addon-api/pi_est.h
@@ -1,6 +0,0 @@-#ifndef EXAMPLES_ASYNC_PI_ESTIMATE_PI_EST_H_-#define EXAMPLES_ASYNC_PI_ESTIMATE_PI_EST_H_--double Estimate(int points);--#endif // EXAMPLES_ASYNC_PI_ESTIMATE_PI_EST_H_
async_pi_estimate/node-addon-api/sync.cc
@@ -1,12 +0,0 @@-#include "sync.h" // NOLINT(build/include)-#include <napi.h>-#include "pi_est.h" // NOLINT(build/include)--// Simple synchronous access to the `Estimate()` function-Napi::Value CalculateSync(const Napi::CallbackInfo& info) {- // expect a number as the first argument- int points = info[0].As<Napi::Number>().Uint32Value();- double est = Estimate(points);-- return Napi::Number::New(info.Env(), est);-}
async_pi_estimate/node-addon-api/sync.h
@@ -1,8 +0,0 @@-#ifndef EXAMPLES_ASYNC_PI_ESTIMATE_SYNC_H_-#define EXAMPLES_ASYNC_PI_ESTIMATE_SYNC_H_--#include <napi.h>--Napi::Value CalculateSync(const Napi::CallbackInfo& info);--#endif // EXAMPLES_ASYNC_PI_ESTIMATE_SYNC_H_
async_work_promise/napi/binding.c
@@ -1,170 +0,0 @@-#include <stdio.h>-#include <stdlib.h>-#include <node_api.h>--// Limit ourselves to this many primes, starting at 2-#define PRIME_COUNT 10-#define CHECK(expr) \- { \- if ((expr) == 0) { \- fprintf(stderr, "%s:%d: failed assertion `%s'\n", __FILE__, __LINE__, #expr); \- fflush(stderr); \- abort(); \- } \- }--typedef struct linked_box_s linked_box_t;-struct linked_box_s {- linked_box_t* prev;- int the_prime;-};--typedef struct {- napi_async_work work;- napi_deferred deferred;- int data[PRIME_COUNT];-} AddonData;--// This function runs on a worker thread. It has no access to the JavaScript.-static void ExecuteWork(napi_env env, void* data) {- AddonData* addon_data = (AddonData*)data;- int idx_inner, idx_outer;- int prime_count = 0;-- // Find the first 1000 prime numbers using an extremely inefficient algorithm.- for (idx_outer = 2; prime_count < PRIME_COUNT; idx_outer++) {- for (idx_inner = 2; idx_inner < idx_outer; idx_inner++) {- if (idx_outer % idx_inner == 0) {- break;- }- }- if (idx_inner < idx_outer) {- continue;- }-- // Save the prime number to the heap. The JavaScript marshaller (CallJs)- // will free this item after having sent it to JavaScript.- addon_data->data[prime_count] = idx_outer;- ++prime_count;- }-}--// This function runs on the main thread after `ExecuteWork` exits.-static void WorkComplete(napi_env env, napi_status status, void* data) {- if (status != napi_ok) {- return;- }-- AddonData* addon_data = (AddonData*)data;- napi_value array;- CHECK(napi_create_array(env, &array) == napi_ok);-- for (uint32_t idx = 0; idx < PRIME_COUNT; idx++) {- napi_value js_the_prime;-- // Convert the integer to a napi_value.- CHECK(napi_create_int32(env, addon_data->data[idx], &js_the_prime) == napi_ok);-- // Store the js value in the array.- CHECK(napi_set_element(env, array, idx, js_the_prime) == napi_ok);- }- CHECK(napi_resolve_deferred(env, addon_data->deferred, array) == napi_ok);-- // Clean up the work item associated with this run.- CHECK(napi_delete_async_work(env, addon_data->work) == napi_ok);-- // Set both values to NULL so JavaScript can order a new run of the thread.- addon_data->work = NULL;- addon_data->deferred = NULL;-}--// Create a deferred promise and an async queue work item.-static napi_value StartWork(napi_env env, napi_callback_info info) {- napi_value work_name, promise;- AddonData* addon_data;-- // Retrieve the per-addon data.- CHECK(napi_get_cb_info(env,- info,- NULL,- NULL,- NULL,- (void**)(&addon_data)) == napi_ok);-- // Ensure that no work is currently in progress.- CHECK(addon_data->work == NULL && "Only one work item must exist at a time");-- // Create a string to describe this asynchronous operation.- CHECK(napi_create_string_utf8(env,- "Node-API Deferred Promise from Async Work Item",- NAPI_AUTO_LENGTH,- &work_name) == napi_ok);-- // Create a deferred promise which we will resolve at the completion of the work.- CHECK(napi_create_promise(env,- &(addon_data->deferred),- &promise) == napi_ok);-- // Create an async work item, passing in the addon data, which will give the- // worker thread access to the above-created deferred promise.- CHECK(napi_create_async_work(env,- NULL,- work_name,- ExecuteWork,- WorkComplete,- addon_data,- &(addon_data->work)) == napi_ok);-- // Queue the work item for execution.- CHECK(napi_queue_async_work(env, addon_data->work) == napi_ok);-- // This causes created `promise` to be returned to JavaScript.- return promise;-}--// Free the per-addon-instance data.-static void addon_getting_unloaded(napi_env env, void* data, void* hint) {- AddonData* addon_data = (AddonData*)data;- CHECK(addon_data->work == NULL &&- "No work item in progress at module unload");- free(addon_data);-}--// The commented-out return type and the commented out formal function-// parameters below help us keep in mind the signature of the addon-// initialization function. We write the body as though the return value were as-// commented below and as though there were parameters passed in as commented-// below.-/*napi_value*/ NAPI_MODULE_INIT(/*napi_env env, napi_value exports*/) {-- // Define addon-level data associated with this instance of the addon.- AddonData* addon_data = (AddonData*)malloc(sizeof(*addon_data));- addon_data->work = NULL;-- // Define the properties that will be set on exports.- napi_property_descriptor start_work = {- "startWork",- NULL,- StartWork,- NULL,- NULL,- NULL,- napi_default,- addon_data- };-- // Decorate exports with the above-defined properties.- CHECK(napi_define_properties(env, exports, 1, &start_work) == napi_ok);-- // Associate the addon data with the exports object, to make sure that when- // the addon gets unloaded our data gets freed.- CHECK(napi_wrap(env,- exports,- addon_data,- addon_getting_unloaded,- NULL,- NULL) == napi_ok);-- // Return the decorated exports object.- return exports;-}
async_work_promise/napi/binding.gyp
@@ -1,8 +0,0 @@-{- 'targets': [- {- 'target_name': 'binding',- 'sources': [ 'binding.c' ]- }- ]-}
async_work_promise/napi/index.js
@@ -1,10 +0,0 @@-// Use the "bindings" package to locate the native bindings.-const binding = require('bindings')('binding');--// Call the function "startWork" which the native bindings library exposes.-// The function returns a promise which will be resolved at the complete of the-// work with an array of worked out primes. This resolution simply prints them out.-binding.startWork()- .then((thePrimes) => {- console.log("Received primes from completed work: " + thePrimes)- });
async_work_promise/napi/package.json
@@ -1,17 +0,0 @@-{- "name": "async_work_promise",- "version": "0.0.0",- "description": "Calling into JS from the thread pool",- "main": "index.js",- "private": true,- "dependencies": {- "bindings": "~1.5.0"- },- "engines": {- "node": ">= 10.6.0"- },- "scripts": {- "test": "node index.js"- },- "gypfile": true-}
async_work_promise/node-addon-api/addon.cc
@@ -1,32 +0,0 @@-#include "napi.h"-#include "worker.h"--Napi::Value DoHeavyMath(const Napi::CallbackInfo& info) {- Napi::Env env = info.Env();-- if (!info[0].IsNumber()) {- Napi::TypeError::New(env, "num1 must be a number")- .ThrowAsJavaScriptException();- return env.Undefined();- }- uint32_t num_1 = info[0].As<Napi::Number>().Uint32Value();- - if (!info[1].IsNumber()) {- Napi::TypeError::New(env, "num2 must be a number")- .ThrowAsJavaScriptException();- return env.Undefined();- }- uint32_t num_2 = info[1].As<Napi::Number>().Uint32Value();- - DoHeavyMathWorker* worker = new DoHeavyMathWorker(env, num_1, num_2);- worker->Queue();- return worker->GetPromise();-}--Napi::Object Init(Napi::Env env, Napi::Object exports) {- exports.Set(Napi::String::New(env, "doHeavyMath"),- Napi::Function::New(env, DoHeavyMath));- return exports;-}--NODE_API_MODULE(NODE_GYP_MODULE_NAME, Init)
async_work_promise/node-addon-api/binding.gyp
@@ -1,32 +0,0 @@-{- "targets": [{- "target_name": "addon",- "sources": [- "addon.cc"- ],- "include_dirs": [- "<!@(node -p \"require('node-addon-api').include\")"- ],- 'defines': [ 'NAPI_CPP_EXCEPTIONS' ],- 'conditions': [- [ 'OS=="win"', {- 'defines': [ '_HAS_EXCEPTIONS=1' ],- 'msvs_settings': {- 'VCCLCompilerTool': {- 'ExceptionHandling': 1,- },- },- }],- [ 'OS=="linux"', {- "cflags": [ "-fexceptions" ],- "cflags_cc": [ "-fexceptions" ],- }],- [ 'OS=="mac"', {- 'xcode_settings': {- 'GCC_ENABLE_CPP_EXCEPTIONS': 'YES',- 'CLANG_CXX_LIBRARY': 'libc++',- }- }]- ],- }]-}
async_work_promise/node-addon-api/index.js
@@ -1,3 +0,0 @@-const bindings = require("bindings")("addon");--bindings.doHeavyMath(2, 1).then(console.log);
async_work_promise/node-addon-api/package.json
@@ -1,15 +0,0 @@-{- "name": "async_worker_promise",- "version": "0.0.0",- "description": "AsyncWorker example that returns a Promise",- "main": "index.js",- "scripts": {- "test": "node ."- },- "author": "Matthew Keil (matthewkeil)",- "gypfile": true,- "dependencies": {- "bindings": "^1.5.0",- "node-addon-api": "^7.0.0"- }-}
async_work_promise/node-addon-api/worker.h
@@ -1,43 +0,0 @@-#include "napi.h"--class DoHeavyMathWorker : public Napi::AsyncWorker {- public:- DoHeavyMathWorker(const Napi::Env& env, uint32_t num_1, uint32_t num_2)- : Napi::AsyncWorker{env, "DoHeavyMathWorker"},- m_deferred{env},- m_num_1{num_1},- m_num_2{num_2} {}-- /**- * GetPromise associated with _deferred for return to JS- */- Napi::Promise GetPromise() { return m_deferred.Promise(); }-- protected:- /**- * Simulate heavy math work- */- void Execute() {- if (m_num_2 == 0) {- SetError("Cannot divide by zero");- return;- }- m_result = m_num_1 / m_num_2;- }-- /**- * Resolve the promise with the result- */- void OnOK() { m_deferred.Resolve(Napi::Number::New(Env(), m_result)); }-- /**- * Reject the promise with errors- */- void OnError(const Napi::Error& err) { m_deferred.Reject(err.Value()); }-- private:- Napi::Promise::Deferred m_deferred;- uint32_t m_num_1;- uint32_t m_num_2;- uint32_t m_result;-};
async_work_thread_safe_function/napi/binding.c
@@ -1,235 +0,0 @@-#include <assert.h>-#include <stdlib.h>-#define NAPI_EXPERIMENTAL-#include <node_api.h>--// Limit ourselves to this many primes, starting at 2-#define PRIME_COUNT 100000-#define REPORT_EVERY 1000--typedef struct {- napi_async_work work;- napi_threadsafe_function tsfn;-} AddonData;---// This function is responsible for converting data coming in from the worker-// thread to napi_value items that can be passed into JavaScript, and for-// calling the JavaScript function.-static void CallJs(napi_env env, napi_value js_cb, void* context, void* data) {- // This parameter is not used.- (void) context;- napi_status status;-- // Retrieve the prime from the item created by the worker thread.- int the_prime = *(int*)data;-- // env and js_cb may both be NULL if Node.js is in its cleanup phase, and- // items are left over from earlier thread-safe calls from the worker thread.- // When env is NULL, we simply skip over the call into Javascript and free the- // items.- if (env != NULL) {- napi_value undefined, js_the_prime;-- // Convert the integer to a napi_value.- status = napi_create_int32(env, the_prime, &js_the_prime);- assert(status == napi_ok);-- // Retrieve the JavaScript `undefined` value so we can use it as the `this`- // value of the JavaScript function call.- status = napi_get_undefined(env, &undefined);- assert(status == napi_ok);-- // Call the JavaScript function and pass it the prime that the secondary- // thread found.- status = napi_call_function(env,- undefined,- js_cb,- 1,- &js_the_prime,- NULL);- assert(status == napi_ok);- }-- // Free the item created by the worker thread.- free(data);-}--// This function runs on a worker thread. It has no access to the JavaScript-// environment except through the thread-safe function.-static void ExecuteWork(napi_env env, void* data) {- AddonData* addon_data = (AddonData*)data;- int idx_inner, idx_outer;- int prime_count = 0;- napi_status status;-- // We bracket the use of the thread-safe function by this thread by a call to- // napi_acquire_threadsafe_function() here, and by a call to- // napi_release_threadsafe_function() immediately prior to thread exit.- status = napi_acquire_threadsafe_function(addon_data->tsfn);- assert(status == napi_ok);-- // Find the first 1000 prime numbers using an extremely inefficient algorithm.- for (idx_outer = 2; prime_count < PRIME_COUNT; idx_outer++) {- for (idx_inner = 2; idx_inner < idx_outer; idx_inner++) {- if (idx_outer % idx_inner == 0) {- break;- }- }- if (idx_inner < idx_outer) {- continue;- }-- // We found a prime. If it's the tenth since the last time we sent one to- // JavaScript, send it to JavaScript.- if (!(++prime_count % REPORT_EVERY)) {-- // Save the prime number to the heap. The JavaScript marshaller (CallJs)- // will free this item after having sent it to JavaScript.- int* the_prime = malloc(sizeof(*the_prime));- *the_prime = idx_outer;-- // Initiate the call into JavaScript. The call into JavaScript will not- // have happened when this function returns, but it will be queued.- status = napi_call_threadsafe_function(addon_data->tsfn,- the_prime,- napi_tsfn_blocking);- assert(status == napi_ok);- }- }-- // Indicate that this thread will make no further use of the thread-safe function.- status = napi_release_threadsafe_function(addon_data->tsfn,- napi_tsfn_release);- assert(status == napi_ok);-}--// This function runs on the main thread after `ExecuteWork` exits.-static void WorkComplete(napi_env env, napi_status status, void* data) {- AddonData* addon_data = (AddonData*)data;-- // Clean up the thread-safe function and the work item associated with this- // run.- status = napi_release_threadsafe_function(addon_data->tsfn,- napi_tsfn_release);- assert(status == napi_ok);- status = napi_delete_async_work(env, addon_data->work);- assert(status == napi_ok);-- // Set both values to NULL so JavaScript can order a new run of the thread.- addon_data->work = NULL;- addon_data->tsfn = NULL;-}--// Create a thread-safe function and an async queue work item. We pass the-// thread-safe function to the async queue work item so the latter might have a-// chance to call into JavaScript from the worker thread on which the-// ExecuteWork callback runs.-static napi_value StartThread(napi_env env, napi_callback_info info) {- size_t argc = 1;- napi_value js_cb, work_name;- AddonData* addon_data;- napi_status status;-- // Retrieve the JavaScript callback we should call with items generated by the- // worker thread, and the per-addon data.- status = napi_get_cb_info(env,- info,- &argc,- &js_cb,- NULL,- (void**)(&addon_data));- assert(status == napi_ok);-- // Ensure that no work is currently in progress.- assert(addon_data->work == NULL && "Only one work item must exist at a time");-- // Create a string to describe this asynchronous operation.- status = napi_create_string_utf8(env,- "Node-API Thread-safe Call from Async Work Item",- NAPI_AUTO_LENGTH,- &work_name);- assert(status == napi_ok);-- // Convert the callback retrieved from JavaScript into a thread-safe function- // which we can call from a worker thread.- status = napi_create_threadsafe_function(env,- js_cb,- NULL,- work_name,- 0,- 1,- NULL,- NULL,- NULL,- CallJs,- &(addon_data->tsfn));- assert(status == napi_ok);-- // Create an async work item, passing in the addon data, which will give the- // worker thread access to the above-created thread-safe function.- status = napi_create_async_work(env,- NULL,- work_name,- ExecuteWork,- WorkComplete,- addon_data,- &(addon_data->work));- assert(status == napi_ok);-- // Queue the work item for execution.- status = napi_queue_async_work(env, addon_data->work);- assert(status == napi_ok);-- // This causes `undefined` to be returned to JavaScript.- return NULL;-}--// Free the per-addon-instance data.-static void addon_getting_unloaded(napi_env env, void* data, void* hint) {- AddonData* addon_data = (AddonData*)data;- assert(addon_data->work == NULL &&- "No work item in progress at module unload");- free(addon_data);-}--// The commented-out return type and the commented out formal function-// parameters below help us keep in mind the signature of the addon-// initialization function. We write the body as though the return value were as-// commented below and as though there were parameters passed in as commented-// below.-/*napi_value*/ NAPI_MODULE_INIT(/*napi_env env, napi_value exports*/) {- napi_status status;- // Define addon-level data associated with this instance of the addon.- AddonData* addon_data = (AddonData*)malloc(sizeof(*addon_data));- addon_data->work = NULL;-- // Define the properties that will be set on exports.- napi_property_descriptor start_work = {- "startThread",- NULL,- StartThread,- NULL,- NULL,- NULL,- napi_default,- addon_data- };-- // Decorate exports with the above-defined properties.- status = napi_define_properties(env, exports, 1, &start_work);- assert(status == napi_ok);-- // Associate the addon data with the exports object, to make sure that when- // the addon gets unloaded our data gets freed.- status = napi_wrap(env,- exports,- addon_data,- addon_getting_unloaded,- NULL,- NULL);- assert(status == napi_ok);-- // Return the decorated exports object.- return exports;-}
async_work_thread_safe_function/napi/binding.gyp
@@ -1,8 +0,0 @@-{- 'targets': [- {- 'target_name': 'binding',- 'sources': [ 'binding.c' ]- }- ]-}
async_work_thread_safe_function/napi/index.js
@@ -1,8 +0,0 @@-// Use the "bindings" package to locate the native bindings.-const binding = require('bindings')('binding');--// Call the function "startThread" which the native bindings library exposes.-// The function accepts a callback which it will call from the worker thread and-// into which it will pass prime numbers. This callback simply prints them out.-binding.startThread((thePrime) =>- console.log("Received prime from secondary thread: " + thePrime));
async_work_thread_safe_function/napi/package.json
@@ -1,17 +0,0 @@-{- "name": "async_work_thread_safe_function",- "version": "0.0.0",- "description": "Calling into JS from the thread pool",- "main": "index.js",- "private": true,- "dependencies": {- "bindings": "~1.2.1"- },- "engines": {- "node": ">= 10.6.0"- },- "scripts": {- "test": "node index.js"- },- "gypfile": true-}
build_with_cmake/README.md
@@ -1,33 +0,0 @@-## Building Node-API Addons Using CMake.js--### Examples--The objective of these examples is to demonstrate how to build Node-API addons using [CMake.js](https://github.com/cmake-js/cmake-js#readme).--These example projects assume that CMake.js has been installed globally:--```-npm install -g cmake-js-cmake-js --help-```--Then, in each of the `napi` and `node-addon-api` directories, the following commands build and test each addon:--```-npm install-npm test-```--Complete CMake.js documentation can be found on the [CMake.js GitHub repository](https://github.com/cmake-js/cmake-js#readme).--### NAPI_VERSION--When building Node-API addons, it's important to specify to the build system the Node-API version your code is designed to work with. With CMake.js, this information is specified in the `CMakeLists.txt` file: --```-add_definitions(-DNAPI_VERSION=3)-```--Since Node-API is ABI-stable, your Node-API addon will work, without recompilation, with the Node-API version you specify in `NAPI_VERSION` and all subsequent Node-API versions. --In the absence of a need for features available only in a specific Node-API version, version 3 is a good choice as it is the version of Node-API that was active when Node-API left experimental status.
build_with_cmake/napi/CMakeLists.txt
@@ -1,13 +0,0 @@-cmake_minimum_required(VERSION 3.9)-cmake_policy(SET CMP0042 NEW)-set (CMAKE_CXX_STANDARD 11)--project (build-napi-with-cmake)-include_directories(${CMAKE_JS_INC})-file(GLOB SOURCE_FILES "hello.c")-add_library(${PROJECT_NAME} SHARED ${SOURCE_FILES} ${CMAKE_JS_SRC})-set_target_properties(${PROJECT_NAME} PROPERTIES PREFIX "" SUFFIX ".node")-target_link_libraries(${PROJECT_NAME} ${CMAKE_JS_LIB})--# Define NAPI_VERSION-add_definitions(-DNAPI_VERSION=3)
build_with_cmake/napi/hello.c
@@ -1,23 +0,0 @@-#include <assert.h>-#include <node_api.h>--static napi_value Method(napi_env env, napi_callback_info info) {- napi_status status;- napi_value world;- status = napi_create_string_utf8(env, "Hello, world!", 13, &world);- assert(status == napi_ok);- return world;-}--#define DECLARE_NAPI_METHOD(name, func) \- { name, 0, func, 0, 0, 0, napi_default, 0 }--static napi_value Init(napi_env env, napi_value exports) {- napi_status status;- napi_property_descriptor desc = DECLARE_NAPI_METHOD("hello", Method);- status = napi_define_properties(env, exports, 1, &desc);- assert(status == napi_ok);- return exports;-}--NAPI_MODULE(hello, Init)
build_with_cmake/napi/hello.js
@@ -1,3 +0,0 @@-var addon = require('bindings')('build-napi-with-cmake');--console.log(addon.hello()); // 'world'
build_with_cmake/napi/package.json
@@ -1,14 +0,0 @@-{- "name": "build-napi-with-cmake",- "version": "0.0.0",- "description": "Build Node-API native addon with CMake.",- "main": "hello.js",- "private": true,- "dependencies": {- "bindings": "~1.2.1"- },- "scripts": {- "install": "cmake-js compile",- "test": "node hello.js"- }-}
build_with_cmake/node-addon-api/CMakeLists.txt
@@ -1,22 +0,0 @@-cmake_minimum_required(VERSION 3.9)-cmake_policy(SET CMP0042 NEW)-set (CMAKE_CXX_STANDARD 11)--project (build-node-addon-api-with-cmake)-include_directories(${CMAKE_JS_INC})-file(GLOB SOURCE_FILES "hello.cc")-add_library(${PROJECT_NAME} SHARED ${SOURCE_FILES} ${CMAKE_JS_SRC})-set_target_properties(${PROJECT_NAME} PROPERTIES PREFIX "" SUFFIX ".node")-target_link_libraries(${PROJECT_NAME} ${CMAKE_JS_LIB})--# Include Node-API wrappers-execute_process(COMMAND node -p "require('node-addon-api').include"- WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}- OUTPUT_VARIABLE NODE_ADDON_API_DIR- )-string(REGEX REPLACE "[\r\n\"]" "" NODE_ADDON_API_DIR ${NODE_ADDON_API_DIR})--target_include_directories(${PROJECT_NAME} PRIVATE ${NODE_ADDON_API_DIR})--# define NAPI_VERSION-add_definitions(-DNAPI_VERSION=3)
build_with_cmake/node-addon-api/hello.cc
@@ -1,22 +0,0 @@-#include <napi.h>---static Napi::String Method(const Napi::CallbackInfo& info) {- // Napi::Env is the opaque data structure containing the environment in which the request is being run. - // We will need this env when we want to create any new objects inside of the node.js environment- Napi::Env env = info.Env();- - // Create a C++ level variable- std::string helloWorld = "Hello, world!";- - // Return a new javascript string that we copy-construct inside of the node.js environment- return Napi::String::New(env, helloWorld);-}--static Napi::Object Init(Napi::Env env, Napi::Object exports) {- exports.Set(Napi::String::New(env, "hello"),- Napi::Function::New(env, Method));- return exports;-}--NODE_API_MODULE(hello, Init)
build_with_cmake/node-addon-api/hello.js
@@ -1,3 +0,0 @@-var addon = require('bindings')('build-node-addon-api-with-cmake');--console.log(addon.hello()); // 'world'
build_with_cmake/node-addon-api/package.json
@@ -1,15 +0,0 @@-{- "name": "build-node-addon-api-with-cmake",- "version": "0.0.0",- "description": "Build Node-API native addon with CMake and node-addon-api C++ wrapper.",- "main": "hello.js",- "private": true,- "dependencies": {- "bindings": "~1.2.1",- "node-addon-api": "^1.0.0"- },- "scripts": {- "install": "cmake-js compile",- "test": "node hello.js"- }-}
call-js-from-async-worker-execute/node-addon-api/binding.gyp
@@ -1,28 +0,0 @@-{- "targets": [- {- "target_name": "dispatcher",- "sources": [ "src/binding.cc" ],- 'cflags!': [ '-fno-exceptions' ],- 'cflags_cc!': [ '-fno-exceptions' ],- 'include_dirs': ["<!@(node -p \"require('node-addon-api').include\")"],- 'dependencies': ["<!(node -p \"require('node-addon-api').gyp\")"],- 'conditions': [- ['OS=="win"', {- "msvs_settings": {- "VCCLCompilerTool": {- "ExceptionHandling": 1- }- }- }],- ['OS=="mac"', {- "xcode_settings": {- "CLANG_CXX_LIBRARY": "libc++",- 'GCC_ENABLE_CPP_EXCEPTIONS': 'YES',- 'MACOSX_DEPLOYMENT_TARGET': '10.7'- }- }]- ]- }- ]-}
call-js-from-async-worker-execute/node-addon-api/index.js
@@ -1,25 +0,0 @@-'use strict'--const { EventEmitter } = require('node:events');-const { dispatch } = require('bindings')('dispatcher');--class Socket extends EventEmitter {- constructor () {- super();- dispatch(this.emit.bind(this));- }-}--const socket = new Socket();--socket.on('open', () => {- console.log('opened');-});--socket.on('message', (message) => {- console.log(`message: ${message}`);-});--socket.on('close', () => {- console.log('closed');-});
call-js-from-async-worker-execute/node-addon-api/package.json
@@ -1,15 +0,0 @@-{- "name": "call-js-from-async-worker-execute",- "version": "0.0.0",- "description": "Node.js Addons - calls JS from AsyncWorker::Execute",- "main": "index.js",- "private": true,- "gypfile": true,- "scripts": {- "start": "node index.js"- },- "dependencies": {- "node-addon-api": "*",- "bindings": "*"- }-}
call-js-from-async-worker-execute/node-addon-api/src/binding.cc
@@ -1,101 +0,0 @@-#include <napi.h>--#include <chrono>-#include <thread>--class Dispatcher : public Napi::AsyncWorker {- public:- Dispatcher(Napi::Env env, Napi::Function& callback)- : Napi::AsyncWorker(env),- callback_(callback),- tsfn_{- Napi::ThreadSafeFunction::New(env, callback, "Dispatcher", 0, 1)} {}-- virtual ~Dispatcher() override { tsfn_.Release(); }-- void Execute() override {- // Since this method executes on a thread that is different from the main- // thread, we can't directly call into JavaScript. To trigger a call into- // JavaScript from this thread, ThreadSafeFunction needs to be used to- // communicate with the main thread, so that the main thread can invoke the- // JavaScript function.-- napi_status status =- tsfn_.BlockingCall([](Napi::Env env, Napi::Function js_callback) {- js_callback.Call({Napi::String::New(env, "open")});- });- if (status != napi_ok) {- SetError("Napi::ThreadSafeNapi::Function.BlockingCall() failed");- return;- }-- std::this_thread::sleep_for(std::chrono::seconds(1));- status = tsfn_.BlockingCall([](Napi::Env env, Napi::Function js_callback) {- js_callback.Call(- {Napi::String::New(env, "message"), Napi::String::New(env, "data1")});- });- if (status != napi_ok) {- SetError("Napi::ThreadSafeNapi::Function.BlockingCall() failed");- return;- }-- std::this_thread::sleep_for(std::chrono::seconds(1));- status = tsfn_.BlockingCall([](Napi::Env env, Napi::Function js_callback) {- js_callback.Call(- {Napi::String::New(env, "message"), Napi::String::New(env, "data2")});- });- if (status != napi_ok) {- SetError("Napi::ThreadSafeNapi::Function.BlockingCall() failed");- return;- }-- std::this_thread::sleep_for(std::chrono::seconds(1));- status = tsfn_.BlockingCall([](Napi::Env env, Napi::Function js_callback) {- js_callback.Call(- {Napi::String::New(env, "message"), Napi::String::New(env, "data3")});- });- if (status != napi_ok) {- SetError("Napi::ThreadSafeNapi::Function.BlockingCall() failed");- return;- }-- status = tsfn_.BlockingCall([](Napi::Env env, Napi::Function js_callback) {- js_callback.Call({Napi::String::New(env, "close")});- });- if (status != napi_ok) {- SetError("Napi::ThreadSafeNapi::Function.BlockingCall() failed");- return;- }- }-- void OnError(Napi::Error const& error) override {- callback_.Call({Napi::String::New(Env(), "error"), error.Value()});- }-- private:- Napi::Function callback_;- Napi::ThreadSafeFunction tsfn_;-};--void Dispatch(const Napi::CallbackInfo& info) {- Napi::Env env = info.Env();-- if (info.Length() < 1 || !info[0].IsFunction()) {- Napi::TypeError::New(- env, "The first argument needs to be the callback function.")- .ThrowAsJavaScriptException();- return;- }-- Napi::Function callback = info[0].As<Napi::Function>();-- Dispatcher* dispatcher = new Dispatcher(env, callback);- dispatcher->Queue();-}--Napi::Object Init(Napi::Env env, Napi::Object exports) {- exports.Set("dispatch", Napi::Function::New(env, Dispatch));- return exports;-}--NODE_API_MODULE(dispatcher, Init)
emit_event_from_cpp/node-addon-api/binding.gyp
@@ -1,30 +0,0 @@-{- "targets": [- {- "target_name": "emit_from_cpp",- "sources": [- "src/emit-from-cpp.cc"- ],- 'cflags!': [ '-fno-exceptions' ],- 'cflags_cc!': [ '-fno-exceptions' ],- 'include_dirs': ["<!@(node -p \"require('node-addon-api').include\")"],- 'dependencies': ["<!(node -p \"require('node-addon-api').gyp\")"],- 'conditions': [- ['OS=="win"', {- "msvs_settings": {- "VCCLCompilerTool": {- "ExceptionHandling": 1- }- }- }],- ['OS=="mac"', {- "xcode_settings": {- "CLANG_CXX_LIBRARY": "libc++",- 'GCC_ENABLE_CPP_EXCEPTIONS': 'YES',- 'MACOSX_DEPLOYMENT_TARGET': '10.7'- }- }]- ]- }- ]-}
emit_event_from_cpp/node-addon-api/index.js
@@ -1,19 +0,0 @@-'use strict'--const EventEmitter = require('events').EventEmitter-const addon = require('bindings')('emit_from_cpp')--const emitter = new EventEmitter()--emitter.on('start', () => {- console.log('### START ...')-})-emitter.on('data', (evt) => {- console.log(evt);-})--emitter.on('end', () => {- console.log('### END ###')-})--addon.callEmit(emitter.emit.bind(emitter))
emit_event_from_cpp/node-addon-api/package.json
@@ -1,15 +0,0 @@-{- "name": "emit-event-from-cpp-example",- "version": "0.0.0",- "description": "Node.js Addons - emit event from C++ to JS",- "main": "index.js",- "private": true,- "gypfile": true,- "scripts": {- "start": "node index.js"- },- "dependencies": {- "node-addon-api": "*",- "bindings": "*"- }-}
emit_event_from_cpp/node-addon-api/src/emit-from-cpp.cc
@@ -1,27 +0,0 @@-#include <napi.h>--#include <chrono>-#include <iostream>-#include <thread>--Napi::Value CallEmit(const Napi::CallbackInfo& info) {- Napi::Env env = info.Env();- Napi::Function emit = info[0].As<Napi::Function>();- emit.Call({Napi::String::New(env, "start")});- for (int i = 0; i < 3; i++) {- std::this_thread::sleep_for(std::chrono::seconds(3));- emit.Call(- {Napi::String::New(env, "data"), Napi::String::New(env, "data ...")});- }- emit.Call({Napi::String::New(env, "end")});- return Napi::String::New(env, "OK");-}--// Init-Napi::Object Init(Napi::Env env, Napi::Object exports) {- exports.Set(Napi::String::New(env, "callEmit"),- Napi::Function::New(env, CallEmit));- return exports;-}--NODE_API_MODULE(NODE_GYP_MODULE_NAME, Init);
function-reference-demo/node-addon-api/binding.gyp
@@ -1,31 +0,0 @@-{- "targets": [- {- "target_name": "addon",- "sources": [- "src/binding.cc",- "src/native-addon.cc"- ],- 'cflags!': [ '-fno-exceptions' ],- 'cflags_cc!': [ '-fno-exceptions' ],- 'include_dirs': ["<!@(node -p \"require('node-addon-api').include\")"],- 'dependencies': ["<!(node -p \"require('node-addon-api').gyp\")"],- 'conditions': [- ['OS=="win"', {- "msvs_settings": {- "VCCLCompilerTool": {- "ExceptionHandling": 1- }- }- }],- ['OS=="mac"', {- "xcode_settings": {- "CLANG_CXX_LIBRARY": "libc++",- 'GCC_ENABLE_CPP_EXCEPTIONS': 'YES',- 'MACOSX_DEPLOYMENT_TARGET': '10.7'- }- }]- ]- }- ]-}
function-reference-demo/node-addon-api/index.js
@@ -1,27 +0,0 @@-'use strict'--const { NativeAddon } = require('bindings')('addon')--function JSFnRef() {- console.log('Hi! I\'m a JS function reference!')-}--function JSFn() {- console.log('Hi! I\'m a JS function!')-}--const ITERATIONS = 5--const addon = new NativeAddon(JSFnRef, JSFn)--for (let i = 0; i < ITERATIONS; i++) {- addon.tryCallByStoredReference()-}--try {- addon.tryCallByStoredFunction()-} catch (err) {- console.error('This code crashes because JSFn is valid only inside the constructor function.')- console.error('The lifespan of the JSFn function is limited to the execution of the constructor function.')- console.error('After that, the function stored in JSFn is ready to be garbage collected and it cannot be used anymore.')-}
function-reference-demo/node-addon-api/package.json
@@ -1,14 +0,0 @@-{- "name": "function-reference-demo",- "version": "0.0.0",- "description": "Tutorial example showing the use of function references.",- "main": "index.js",- "gypfile": true,- "scripts": {- "start": "node index.js"- },- "dependencies": {- "node-addon-api": "*",- "bindings": "*"- }-}
function-reference-demo/node-addon-api/src/binding.cc
@@ -1,9 +0,0 @@-#include <napi.h>-#include "native-addon.h"--Napi::Object Init(Napi::Env env, Napi::Object exports) {- NativeAddon::Init(env, exports);- return exports;-}--NODE_API_MODULE(NODE_GYP_MODULE_NAME, Init)
function-reference-demo/node-addon-api/src/native-addon.cc
@@ -1,36 +0,0 @@-#include "native-addon.h"-#include <iostream>--Napi::FunctionReference NativeAddon::constructor;--Napi::Object NativeAddon::Init(Napi::Env env, Napi::Object exports) {- Napi::Function func =- DefineClass(env,- "NativeAddon",- {InstanceMethod("tryCallByStoredReference",- &NativeAddon::TryCallByStoredReference),- InstanceMethod("tryCallByStoredFunction",- &NativeAddon::TryCallByStoredFunction)});-- constructor = Napi::Persistent(func);- constructor.SuppressDestruct();-- exports.Set("NativeAddon", func);- return exports;-}--NativeAddon::NativeAddon(const Napi::CallbackInfo& info)- : Napi::ObjectWrap<NativeAddon>(info) {- jsFnRef = Napi::Persistent(info[0].As<Napi::Function>());- jsFn = info[1].As<Napi::Function>();-}--void NativeAddon::TryCallByStoredReference(const Napi::CallbackInfo& info) {- // Napi::Env env = info.Env();- jsFnRef.Call({});-}--void NativeAddon::TryCallByStoredFunction(const Napi::CallbackInfo& info) {- // Napi::Env env = info.Env();- jsFn.Call({});-}
function-reference-demo/node-addon-api/src/native-addon.h
@@ -1,15 +0,0 @@-#include <napi.h>--class NativeAddon : public Napi::ObjectWrap<NativeAddon> {- public:- static Napi::Object Init(Napi::Env env, Napi::Object exports);- NativeAddon(const Napi::CallbackInfo& info);-- private:- static Napi::FunctionReference constructor;- Napi::FunctionReference jsFnRef;- Napi::Function jsFn;-- void TryCallByStoredReference(const Napi::CallbackInfo& info);- void TryCallByStoredFunction(const Napi::CallbackInfo& info);-};
inherits_from_event_emitter/node-addon-api/binding.gyp
@@ -1,31 +0,0 @@-{- "targets": [- {- "target_name": "native_emitter",- "sources": [- "src/binding.cc",- "src/native-emitter.cc"- ],- 'cflags!': [ '-fno-exceptions' ],- 'cflags_cc!': [ '-fno-exceptions' ],- 'include_dirs': ["<!@(node -p \"require('node-addon-api').include\")"],- 'dependencies': ["<!(node -p \"require('node-addon-api').gyp\")"],- 'conditions': [- ['OS=="win"', {- "msvs_settings": {- "VCCLCompilerTool": {- "ExceptionHandling": 1- }- }- }],- ['OS=="mac"', {- "xcode_settings": {- "CLANG_CXX_LIBRARY": "libc++",- 'GCC_ENABLE_CPP_EXCEPTIONS': 'YES',- 'MACOSX_DEPLOYMENT_TARGET': '10.7'- }- }]- ]- }- ]-}
inherits_from_event_emitter/node-addon-api/index.js
@@ -1,23 +0,0 @@-'use strict'--const EventEmitter = require('events').EventEmitter-const NativeEmitter = require('bindings')('native_emitter').NativeEmitter-const inherits = require('util').inherits--inherits(NativeEmitter, EventEmitter)--const emitter = new NativeEmitter()--emitter.on('start', () => {- console.log('### START ...')-})--emitter.on('data', (evt) => {- console.log(evt)-})--emitter.on('end', () => {- console.log('### END ###')-})--emitter.callAndEmit()
inherits_from_event_emitter/node-addon-api/package.json
@@ -1,15 +0,0 @@-{- "name": "inherits-from-event-emitter-example",- "version": "0.0.0",- "description": "Node.js Addons - inherits from event emitter",- "main": "index.js",- "private": true,- "gypfile": true,- "scripts": {- "start": "node index.js"- },- "dependencies": {- "node-addon-api": "*",- "bindings": "*"- }-}
inherits_from_event_emitter/node-addon-api/src/binding.cc
@@ -1,9 +0,0 @@-#include <napi.h>-#include "native-emitter.h"--Napi::Object Init(Napi::Env env, Napi::Object exports) {- NativeEmitter::Init(env, exports);- return exports;-}--NODE_API_MODULE(NODE_GYP_MODULE_NAME, Init)
inherits_from_event_emitter/node-addon-api/src/native-emitter.cc
@@ -1,40 +0,0 @@-#include <chrono>-#include <iostream>-#include <thread>--#include "native-emitter.h"--Napi::FunctionReference NativeEmitter::constructor;--Napi::Object NativeEmitter::Init(Napi::Env env, Napi::Object exports) {- Napi::Function func =- DefineClass(env,- "NativeEmitter",- {InstanceMethod("callAndEmit", &NativeEmitter::CallAndEmit)});-- constructor = Napi::Persistent(func);- constructor.SuppressDestruct();-- exports.Set("NativeEmitter", func);- return exports;-}--NativeEmitter::NativeEmitter(const Napi::CallbackInfo& info)- : Napi::ObjectWrap<NativeEmitter>(info) {- // NOOP-}--Napi::Value NativeEmitter::CallAndEmit(const Napi::CallbackInfo& info) {- Napi::Env env = info.Env();- Napi::Function emit =- info.This().As<Napi::Object>().Get("emit").As<Napi::Function>();- emit.Call(info.This(), {Napi::String::New(env, "start")});- for (int i = 0; i < 3; i++) {- std::this_thread::sleep_for(std::chrono::seconds(1));- emit.Call(- info.This(),- {Napi::String::New(env, "data"), Napi::String::New(env, "data ...")});- }- emit.Call(info.This(), {Napi::String::New(env, "end")});- return Napi::String::New(env, "OK");-}
inherits_from_event_emitter/node-addon-api/src/native-emitter.h
@@ -1,12 +0,0 @@-#include <napi.h>--class NativeEmitter : public Napi::ObjectWrap<NativeEmitter> {- public:- static Napi::Object Init(Napi::Env env, Napi::Object exports);- NativeEmitter(const Napi::CallbackInfo& info);-- private:- static Napi::FunctionReference constructor;-- Napi::Value CallAndEmit(const Napi::CallbackInfo& info);-};
multiple_load/napi/binding.gyp
@@ -1,8 +0,0 @@-{- 'targets': [- {- 'target_name': 'multiple_load',- 'sources': [ 'multiple_load.c' ]- }- ]-}
multiple_load/napi/index.js
@@ -1,40 +0,0 @@-// Example illustrating the case where a native addon is loaded multiple times.-// This entire file is executed twice, concurrently - once on the main thread,-// and once on a thread launched from the main thread.--// We load the worker threads module, which allows us to launch multiple Node.js-// environments, each in its own thread.-const {- Worker, isMainThread-} = require('worker_threads');--// We load the native addon.-const addon = require('bindings')('multiple_load');--// The iteration count can be tweaked to ensure that the output from the two-// threads is interleaved. Too few iterations and the output of one thread-// follows the output of the other, not really illustrating the concurrency.-const iterations = 1000;--// This function is an idle loop that performs a random walk from 0 by calling-// into the native addon to either increment or decrement the initial value.-function useAddon(addon, prefix, iterations) {- if (iterations >= 0) {- if (Math.random() < 0.5) {- console.log(prefix + ': new value (decremented): ' + addon.decrement());- } else {- console.log(prefix + ': new value (incremented): ' + addon.increment());- }- setImmediate(() => useAddon(addon, prefix, --iterations));- }-}--if (isMainThread) {- // On the main thread, we launch a worker and wait for it to come online. Then- // we start the loop.- (new Worker(__filename)).on('online',- () => useAddon(addon, "Main thread", iterations));-} else {- // On the secondary thread we immediately start the loop.- useAddon(addon, "Worker thread", iterations);-}
multiple_load/napi/multiple_load.c
@@ -1,145 +0,0 @@-#include <stdio.h>-#include <math.h>-#include <stdlib.h>-#include <node_api.h>--#define CHECK(expr) \- { \- if ((expr) == 0) { \- fprintf(stderr, "%s:%d: failed assertion `%s'\n", __FILE__, __LINE__, #expr); \- fflush(stderr); \- abort(); \- } \- }--// Structure containing information needed for as long as the addon exists. It-// replaces the use of global static data with per-addon-instance data by-// associating an instance of this structure with each instance of this addon-// during addon initialization. The instance of this structure is then passed to-// each binding the addon provides. Thus, the data stored in an instance of this-// structure is available to each binding, just as global static data would be.-typedef struct {- double value;-} AddonData;--// This is the actual, useful work performed: increment or decrement the value-// stored per addon instance after passing it through a CPU-consuming but-// otherwise useless calculation.-static int ModifyAddonData(AddonData* data, double offset) {- // Expensively increment or decrement the value.- data->value = tan(atan(exp(log(sqrt(data->value * data->value))))) + offset;-- // Round the value to the nearest integer.- data->value =- (double)(((int)data->value) +- (data->value - ((double)(int)data->value) > 0.5 ? 1 : 0));-- // Return the value as an integer.- return (int)(data->value);-}--// This is boilerplate. The instance of the `AddonData` structure created during-// addon initialization must be destroyed when the addon is unloaded. This-// function will be called when the addon's `exports` object is garbage collected.-static void DeleteAddonData(napi_env env, void* data, void* hint) {- // Avoid unused parameter warnings.- (void) env;- (void) hint;-- // Free the per-addon-instance data.- free(data);-}--// This is also boilerplate. It creates and initializes an instance of the-// `AddonData` structure and ties its lifecycle to that of the addon instance's-// `exports` object. This means that the data will be available to this instance-// of the addon for as long as the JavaScript engine keeps it alive.-static AddonData* CreateAddonData(napi_env env, napi_value exports) {- AddonData* result = malloc(sizeof(*result));- result->value = 0.0;- CHECK(napi_wrap(env,- exports,- result,- DeleteAddonData,- NULL,- NULL) == napi_ok);- return result;-}--// This function is called from JavaScript. It uses an expensive operation to-// increment the value stored inside the `AddonData` structure by one.-static napi_value Increment(napi_env env, napi_callback_info info) {- // Retrieve the per-addon-instance data.- AddonData* addon_data = NULL;- CHECK(napi_get_cb_info(env,- info,- NULL,- NULL,- NULL,- ((void**)&addon_data)) == napi_ok);-- // Increment the per-addon-instance value and create a new JavaScript integer- // from it.- napi_value result;- CHECK(napi_create_int32(env,- ModifyAddonData(addon_data, 1.0),- &result) == napi_ok);-- // Return the JavaScript integer back to JavaScript.- return result;-}--// This function is called from JavaScript. It uses an expensive operation to-// decrement the value stored inside the `AddonData` structure by one.-static napi_value Decrement(napi_env env, napi_callback_info info) {- // Retrieve the per-addon-instance data.- AddonData* addon_data = NULL;- CHECK(napi_get_cb_info(env,- info,- NULL,- NULL,- NULL,- ((void**)&addon_data)) == napi_ok);-- // Decrement the per-addon-instance value and create a new JavaScript integer- // from it.- napi_value result;- CHECK(napi_create_int32(env,- ModifyAddonData(addon_data, -1.0),- &result) == napi_ok);-- // Return the JavaScript integer back to JavaScript.- return result;-}--// Initialize the addon in such a way that it may be initialized multiple times-// per process. The function body following this macro is provided the value-// `env` which has type `napi_env` and the value `exports` which has type-// `napi_value` and which refers to a JavaScript object that ultimately contains-// the functions this addon wishes to expose. At the end, it must return a-// `napi_value`. It may return `exports`, or it may create a new `napi_value`-// and return that instead.-NAPI_MODULE_INIT(/*env, exports*/) {- // Create a new instance of the per-instance-data that will be associated with- // the instance of the addon being initialized here and that will be destroyed- // along with the instance of the addon.- AddonData* addon_data = CreateAddonData(env, exports);-- // Declare the bindings this addon provides. The data created above is given- // as the last initializer parameter, and will be given to the binding when it- // is called.- napi_property_descriptor bindings[] = {- {"increment", NULL, Increment, NULL, NULL, NULL, napi_enumerable, addon_data},- {"decrement", NULL, Decrement, NULL, NULL, NULL, napi_enumerable, addon_data}- };-- // Expose the two bindings declared above to JavaScript.- CHECK(napi_define_properties(env,- exports,- sizeof(bindings) / sizeof(bindings[0]),- bindings) == napi_ok);-- // Return the `exports` object provided. It now has two new properties, which- // are the functions we wish to expose to JavaScript.- return exports;-}
multiple_load/napi/package.json
@@ -1,17 +0,0 @@-{- "name": "multiple_load",- "version": "0.0.0",- "description": "Multiple load example",- "main": "index.js",- "private": true,- "scripts": {- "test": "node --experimental-worker index.js"- },- "engines": {- "node": ">= 10.10.0"- },- "gypfile": true,- "dependencies": {- "bindings": "~1.2.1"- }-}
multiple_load/node_10/binding.gyp
@@ -1,8 +0,0 @@-{- 'targets': [- {- 'target_name': 'multiple_load',- 'sources': [ 'multiple_load.cc' ]- }- ]-}
multiple_load/node_10/index.js
@@ -1,40 +0,0 @@-// Example illustrating the case where a native addon is loaded multiple times.-// This entire file is executed twice, concurrently - once on the main thread,-// and once on a thread launched from the main thread.--// We load the worker threads module, which allows us to launch multiple Node.js-// environments, each in its own thread.-const {- Worker, isMainThread-} = require('worker_threads');--// We load the native addon.-const addon = require('bindings')('multiple_load');--// The iteration count can be tweaked to ensure that the output from the two-// threads is interleaved. Too few iterations and the output of one thread-// follows the output of the other, not really illustrating the concurrency.-const iterations = 1000;--// This function is an idle loop that performs a random walk from 0 by calling-// into the native addon to either increment or decrement the initial value.-function useAddon(addon, prefix, iterations) {- if (iterations >= 0) {- if (Math.random() < 0.5) {- console.log(prefix + ': new value (decremented): ' + addon.decrement());- } else {- console.log(prefix + ': new value (incremented): ' + addon.increment());- }- setImmediate(() => useAddon(addon, prefix, --iterations));- }-}--if (isMainThread) {- // On the main thread, we launch a worker and wait for it to come online. Then- // we start the loop.- (new Worker(__filename)).on('online',- () => useAddon(addon, "Main thread", iterations));-} else {- // On the secondary thread we immediately start the loop.- useAddon(addon, "Worker thread", iterations);-}
multiple_load/node_10/multiple_load.cc
@@ -1,121 +0,0 @@-#include <math.h>-#include <node.h>--using namespace v8;--// Class containing information needed for as long as the addon exists. It-// replaces the use of global static data with per-addon-instance data by-// associating an instance of this class with each instance of this addon during-// addon initialization. The instance of this class is then passed to each-// binding the addon provides. Thus, the data stored in an instance of this-// class is available to each binding, just as global static data would be.-class AddonData {- public:- // This is the actual, useful work performed: increment or decrement the value- // stored per addon instance after passing it through a CPU-consuming but- // otherwise useless calculation. Round the result to the nearest integer.- int GetNewValue(double offset) {- // Expensively increment or decrement the value.- value = tan(atan(exp(log(sqrt(value * value))))) + offset;-- // Round the value to the nearest integer.- value =- (double)(((int)value) + (value - ((double)(int)value) > 0.5 ? 1 : 0));-- // Return the value as an integer.- return (int)value;- }-- // This is boilerplate. It creates a new instance of this class and wraps it- // into a v8::External. The isolate and the exports are necessary, because we- // want the instance of this class to be destroyed along with the exports- // object when the addon is eventually unloaded.- static Local<Value> New(Isolate* isolate, Local<Object> exports) {- return External::New(isolate, new AddonData(isolate, exports));- }-- private:- // This is the actual, useful payload carried by an instance of this class.- // A double value is kept around for as long as an instance of this addon is- // loaded, and is incremented or decremented whenever the addon receives a- // call from JavaScript.- double value;-- explicit AddonData(Isolate* isolate, Local<Object> exports)- : // The payload is initialized here. The rest of the constructor is- // boilerplate that ensures that the instance of this addon data is- // destroyed along with the instance of this addon (`exports`).- value(0.0) {- exports_persistent.Reset(isolate, exports);- exports_persistent.SetWeak(this, DeleteMe, WeakCallbackType::kParameter);- }-- // The rest of the class definition is boilerplate.-- // The persistent reference must be reset before the instance of this class is- // destroyed, otherwise memory will leak on the V8 side.- ~AddonData() { exports_persistent.Reset(); }-- // This static function will be called when the addon instance is unloaded. It- // merely destroys the per-addon-instance data.- static void DeleteMe(const WeakCallbackInfo<AddonData>& info) {- delete info.GetParameter();- }-- Persistent<Object> exports_persistent;-};--// Function called from JavaScript to increment the value stored in this addon.-void Increment(const FunctionCallbackInfo<Value>& info) {- // Retrieve the per-addon-instance data.- AddonData* addon_data =- static_cast<AddonData*>(info.Data().As<External>()->Value());-- info.GetReturnValue().Set(- Number::New(info.GetIsolate(), addon_data->GetNewValue(1.0)));-}--// Function called from JavaScript to decrement the value stored in this addon.-void Decrement(const FunctionCallbackInfo<Value>& info) {- // Retrieve the per-addon-instance data.- AddonData* addon_data =- static_cast<AddonData*>(info.Data().As<External>()->Value());-- info.GetReturnValue().Set(- Number::New(info.GetIsolate(), addon_data->GetNewValue(-1.0)));-}--// Initialize the addon in such a way that it may be initialized multiple times-// per process. The function body following this macro is provided the value-// `exports` of type `Local<Object>`, the value `module` of type-// `Local<Object>`, and `context` of type `Local<Context>`. It may either define-// new properties on the `exports` object, or define the property named-// "exports" on the `module` object.-NODE_MODULE_INIT(/*exports, module, context*/) {- Isolate* isolate = context->GetIsolate();-- // Create a new instance of the addon data that will be associated with this- // instance of the addon, and that will be freed along with this instance of- // the addon.- Local<Value> addon_data = AddonData::New(isolate, exports);-- // Export the functions we wish to make available to JavaScript.-- exports- ->Set(context,- String::NewFromUtf8(isolate, "increment", NewStringType::kNormal)- .ToLocalChecked(),- FunctionTemplate::New(isolate, Increment, addon_data)- ->GetFunction(context)- .ToLocalChecked())- .FromJust();-- exports- ->Set(context,- String::NewFromUtf8(isolate, "decrement", NewStringType::kNormal)- .ToLocalChecked(),- FunctionTemplate::New(isolate, Decrement, addon_data)- ->GetFunction(context)- .ToLocalChecked())- .FromJust();-}
multiple_load/node_10/package.json
@@ -1,17 +0,0 @@-{- "name": "multiple_load",- "version": "0.0.0",- "description": "Multiple load example",- "main": "index.js",- "private": true,- "scripts": {- "test": "node --experimental-worker index.js"- },- "engines": {- "node": ">= 10.10.0"- },- "gypfile": true,- "dependencies": {- "bindings": "~1.2.1"- }-}
napi-asyncworker-example/node-addon-api/README.md
@@ -1,14 +0,0 @@-# Node-API AsyncWorker Example--This is an example project showing how to use the Node.js Node-API AsyncWorker class--A tutorial describing this project can be found at the [Node-API Resource](https://napi.inspiredware.com/special-topics/asyncworker.html).--To build and run this program on your system, clone it to your computer and run these two commands inside your clone:--```-npm install-npm test-```--> You need to have Node 10.5.0 or later installed.
napi-asyncworker-example/node-addon-api/binding.gyp
@@ -1,20 +0,0 @@-{ - 'targets': [ - { - 'target_name': 'napi-asyncworker-example-native', - 'sources': [ 'src/RunSimpleAsyncWorker.cc', 'src/SimpleAsyncWorker.cc' ], - 'include_dirs': ["<!@(node -p \"require('node-addon-api').include\")"], - 'dependencies': ["<!(node -p \"require('node-addon-api').gyp\")"], - 'cflags!': [ '-fno-exceptions' ], - 'cflags_cc!': [ '-fno-exceptions' ], - 'xcode_settings': { - 'GCC_ENABLE_CPP_EXCEPTIONS': 'YES', - 'CLANG_CXX_LIBRARY': 'libc++', - 'MACOSX_DEPLOYMENT_TARGET': '10.7' - }, - 'msvs_settings': { - 'VCCLCompilerTool': { 'ExceptionHandling': 1 }, - } - } - ] -}
napi-asyncworker-example/node-addon-api/package.json
@@ -1,23 +0,0 @@-{- "name": "napi-asyncworker-example",- "description": "An example project showing how to use the Node.js Node-API AsyncWorker class",- "version": "1.0.0",- "main": "test/Test.js",- "dependencies": {- "node-addon-api": "^1.1.0"- },- "scripts": {- "test": "node --napi-modules ./test/Test.js"- },- "gypfile": true,- "keywords": [- "node",- "napi",- "n-api",- "async",- "asyncworker",- "example"- ],- "author": "Jim Schlight",- "license": "MIT"-}
napi-asyncworker-example/node-addon-api/src/RunSimpleAsyncWorker.cc
@@ -1,19 +0,0 @@-#include "SimpleAsyncWorker.h" - -Value runSimpleAsyncWorker(const CallbackInfo& info) { - int runTime = info[0].As<Number>(); - Function callback = info[1].As<Function>(); - SimpleAsyncWorker* asyncWorker = new SimpleAsyncWorker(callback, runTime); - asyncWorker->Queue(); - std::string msg = - "SimpleAsyncWorker for " + std::to_string(runTime) + " seconds queued."; - return String::New(info.Env(), msg.c_str()); -}; - -Object Init(Env env, Object exports) { - exports["runSimpleAsyncWorker"] = Function::New( - env, runSimpleAsyncWorker, std::string("runSimpleAsyncWorker")); - return exports; -} - -NODE_API_MODULE(addon, Init)
napi-asyncworker-example/node-addon-api/src/SimpleAsyncWorker.cc
@@ -1,17 +0,0 @@-#include "SimpleAsyncWorker.h" -#include <chrono> -#include <thread> - -SimpleAsyncWorker::SimpleAsyncWorker(Function& callback, int runTime) - : AsyncWorker(callback), runTime(runTime){}; - -void SimpleAsyncWorker::Execute() { - std::this_thread::sleep_for(std::chrono::seconds(runTime)); - if (runTime == 4) SetError("Oops! Failed after 'working' 4 seconds."); -}; - -void SimpleAsyncWorker::OnOK() { - std::string msg = "SimpleAsyncWorker returning after 'working' " + - std::to_string(runTime) + " seconds."; - Callback().Call({Env().Null(), String::New(Env(), msg)}); -};
napi-asyncworker-example/node-addon-api/src/SimpleAsyncWorker.h
@@ -1,15 +0,0 @@-#pragma once -#include <napi.h> -using namespace Napi; - -class SimpleAsyncWorker : public AsyncWorker { - public: - SimpleAsyncWorker(Function& callback, int runTime); - virtual ~SimpleAsyncWorker(){}; - - void Execute(); - void OnOK(); - - private: - int runTime; -};
napi-asyncworker-example/node-addon-api/test/Test.js
@@ -1,18 +0,0 @@-const runWorker = require('../build/Release/napi-asyncworker-example-native'); - -let result = runWorker.runSimpleAsyncWorker(2, AsyncWorkerCompletion); -console.log("runSimpleAsyncWorker returned '"+result+"'."); - -result = runWorker.runSimpleAsyncWorker(4, AsyncWorkerCompletion); -console.log("runSimpleAsyncWorker returned '"+result+"'."); - -result = runWorker.runSimpleAsyncWorker(8, AsyncWorkerCompletion); -console.log("runSimpleAsyncWorker returned '"+result+"'."); - -function AsyncWorkerCompletion (err, result) { - if (err) { - console.log("SimpleAsyncWorker returned an error: ", err); - } else { - console.log("SimpleAsyncWorker returned '"+result+"'."); - } -};
object-template-demo/README.md
@@ -1,3 +0,0 @@-The goal of this demo is to show how to implement V8 ObjectTemplate-like handlers.-NAN uses the ObjectTemplate directly: the code is adapted from NAN namedinterceptors unit test.-Node-API cannot use the ObjectTemplate: it uses the JavaScript Proxy object to get similar behavior.
object-template-demo/nan/binding.gyp
@@ -1,11 +0,0 @@-{- "targets": [- {- "target_name": "object-template-demo",- "sources": [ "object-template-demo.cc" ],- "include_dirs": [- "<!(node -e \"require('nan')\")"- ]- }- ]-}
object-template-demo/nan/index.js
@@ -1,10 +0,0 @@-const addon = require('bindings')('object-template-demo');--const interceptor = addon.create();-console.log(interceptor.prop); // 'foo'-interceptor.prop = 'setting a value';-console.log(interceptor.prop); // 'setting a value'-delete interceptor.something;-console.log(interceptor.prop); // 'goober';-console.log(Object.prototype.hasOwnProperty.call(interceptor, "thing")); // true-console.log(Object.keys(interceptor)[0]); // 'value'
object-template-demo/nan/object-template-demo.cc
@@ -1,122 +0,0 @@-/*********************************************************************- * NAN - Native Abstractions for Node.js- *- * Copyright (c) 2018 NAN contributors- *- * MIT License <https://github.com/nodejs/nan/blob/master/LICENSE.md>- ********************************************************************/--#include <nan.h>-#include <cstring>--using namespace Nan; // NOLINT(build/namespaces)--class NamedInterceptor : public ObjectWrap {- char buf[256];-- public:- NamedInterceptor() { std::strncpy(this->buf, "foo", sizeof (this->buf)); }- static NAN_MODULE_INIT(Init);- static v8::Local<v8::Value> NewInstance ();- static NAN_METHOD(New);-- static NAN_PROPERTY_GETTER(PropertyGetter);- static NAN_PROPERTY_SETTER(PropertySetter);- static NAN_PROPERTY_ENUMERATOR(PropertyEnumerator);- static NAN_PROPERTY_DELETER(PropertyDeleter);- static NAN_PROPERTY_QUERY(PropertyQuery);-};--static Persistent<v8::FunctionTemplate> namedinterceptors_constructor;--NAN_METHOD(CreateNew) {- info.GetReturnValue().Set(NamedInterceptor::NewInstance());-}--NAN_MODULE_INIT(NamedInterceptor::Init) {- v8::Local<v8::FunctionTemplate> tpl =- Nan::New<v8::FunctionTemplate>(NamedInterceptor::New);- namedinterceptors_constructor.Reset(tpl);- tpl->SetClassName(Nan::New("NamedInterceptor").ToLocalChecked());- tpl->InstanceTemplate()->SetInternalFieldCount(1);- v8::Local<v8::ObjectTemplate> inst = tpl->InstanceTemplate();-- SetNamedPropertyHandler(- inst- , NamedInterceptor::PropertyGetter- , NamedInterceptor::PropertySetter- , NamedInterceptor::PropertyQuery- , NamedInterceptor::PropertyDeleter- , NamedInterceptor::PropertyEnumerator);-- v8::Local<v8::Function> createnew =- Nan::GetFunction(Nan::New<v8::FunctionTemplate>(CreateNew))- .ToLocalChecked();- Set(target, Nan::New("create").ToLocalChecked(), createnew);-}--v8::Local<v8::Value> NamedInterceptor::NewInstance () {- EscapableHandleScope scope;- v8::Local<v8::FunctionTemplate> constructorHandle =- Nan::New(namedinterceptors_constructor);- v8::Local<v8::Object> instance =- Nan::NewInstance(GetFunction(constructorHandle).ToLocalChecked())- .ToLocalChecked();- return scope.Escape(instance);-}--NAN_METHOD(NamedInterceptor::New) {- NamedInterceptor* interceptor = new NamedInterceptor();- interceptor->Wrap(info.This());- info.GetReturnValue().Set(info.This());-}---NAN_PROPERTY_GETTER(NamedInterceptor::PropertyGetter) {- NamedInterceptor* interceptor =- ObjectWrap::Unwrap<NamedInterceptor>(info.Holder());- if (!std::strcmp(*Nan::Utf8String(property), "prop")) {- info.GetReturnValue().Set(Nan::New(interceptor->buf).ToLocalChecked());- } else {- info.GetReturnValue().Set(Nan::New("bar").ToLocalChecked());- }-}--NAN_PROPERTY_SETTER(NamedInterceptor::PropertySetter) {- NamedInterceptor* interceptor =- ObjectWrap::Unwrap<NamedInterceptor>(info.Holder());- if (!std::strcmp(*Nan::Utf8String(property), "prop")) {- std::strncpy(- interceptor->buf- , *Nan::Utf8String(value)- , sizeof (interceptor->buf));- info.GetReturnValue().Set(info.This());- } else {- info.GetReturnValue().Set(info.This());- }-}--NAN_PROPERTY_ENUMERATOR(NamedInterceptor::PropertyEnumerator) {- v8::Local<v8::Array> arr = Nan::New<v8::Array>();- Set(arr, 0, Nan::New("value").ToLocalChecked());- info.GetReturnValue().Set(arr);-}--NAN_PROPERTY_DELETER(NamedInterceptor::PropertyDeleter) {- NamedInterceptor* interceptor =- ObjectWrap::Unwrap<NamedInterceptor>(info.Holder());- std::strncpy(interceptor->buf, "goober", sizeof (interceptor->buf));- info.GetReturnValue().Set(True());-}--NAN_PROPERTY_QUERY(NamedInterceptor::PropertyQuery) {- Nan::Utf8String s(property);- if (!std::strcmp(*s, "thing")) {- return info.GetReturnValue().Set(Nan::New<v8::Integer>(v8::DontEnum));- }- if (!std::strcmp(*s, "value")) {- return info.GetReturnValue().Set(Nan::New(0));- }-}--NODE_MODULE(namedinterceptors, NamedInterceptor::Init)
object-template-demo/nan/package.json
@@ -1,15 +0,0 @@-{- "name": "object-template-demo",- "version": "0.0.0",- "description": "Intercept named property access using V8 ObjectTemplate",- "main": "index.js",- "private": true,- "gypfile": true,- "scripts": {- "test": "node index.js"- },- "dependencies": {- "bindings": "~1.5.0",- "nan": "^2.14.0"- }-}
object-template-demo/napi/binding.gyp
@@ -1,8 +0,0 @@-{- "targets": [- {- "target_name": "object_template_demo",- "sources": [ "object-template-demo.cc", "proxy-template.cc" ]- }- ]-}
object-template-demo/napi/index.js
@@ -1,10 +0,0 @@-const addon = require('bindings')('object_template_demo');--const interceptor = addon.create();-console.log(interceptor.prop); // 'foo'-interceptor.prop = 'setting a value';-console.log(interceptor.prop); // 'setting a value'-delete interceptor.something;-console.log(interceptor.prop); // 'goober';-console.log(Object.prototype.hasOwnProperty.call(interceptor, "thing")); // true-console.log(Object.keys(interceptor)[0]); // 'value'
object-template-demo/napi/node-api-common.h
@@ -1,141 +0,0 @@-#include <node_api.h>-#include <initializer_list>-#include <utility>--// Empty value so that macros here are able to return NULL or void-#define NODE_API_RETVAL_NOTHING // Intentionally blank #define--#define GET_AND_THROW_LAST_ERROR(env) \- do { \- const napi_extended_error_info* error_info; \- napi_get_last_error_info((env), &error_info); \- bool is_pending; \- const char* err_message = error_info->error_message; \- napi_is_exception_pending((env), &is_pending); \- /* If an exception is already pending, don't rethrow it */ \- if (!is_pending) { \- const char* error_message = \- err_message != NULL ? err_message : "empty error message"; \- napi_throw_error((env), NULL, error_message); \- } \- } while (0)--#define NODE_API_ASSERT_BASE(env, assertion, message, ret_val) \- do { \- if (!(assertion)) { \- napi_throw_error( \- (env), NULL, "assertion (" #assertion ") failed: " message); \- return ret_val; \- } \- } while (0)--// Returns NULL on failed assertion.-// This is meant to be used inside napi_callback methods.-#define NODE_API_ASSERT(env, assertion, message) \- NODE_API_ASSERT_BASE(env, assertion, message, NULL)--#define NODE_API_CALL_BASE(env, the_call, ret_val) \- do { \- if ((the_call) != napi_ok) { \- GET_AND_THROW_LAST_ERROR((env)); \- return ret_val; \- } \- } while (0)--// Returns NULL if the_call doesn't return napi_ok.-#define NODE_API_CALL(env, the_call) NODE_API_CALL_BASE(env, the_call, NULL)--// Returns empty if the_call doesn't return napi_ok.-#define NODE_API_CALL_RETURN_VOID(env, the_call) \- NODE_API_CALL_BASE(env, the_call, NODE_API_RETVAL_NOTHING)--#define CHECK_NAPI(...) \- do { \- napi_status res__ = (__VA_ARGS__); \- if (res__ != napi_ok) { \- return res__; \- } \- } while (0)--#define NAPI_CALL(expr) NODE_API_CALL(env, expr);--#ifdef __cpp_lib_span-#include <span>-using std::span;-#else-/**- * @brief A span of values that can be used to pass arguments to function.- *- * For C++20 we should consider to replace it with std::span.- */-template <typename T>-struct span {- constexpr span(std::initializer_list<T> il) noexcept- : data_{const_cast<T*>(il.begin())}, size_{il.size()} {}- constexpr span(T* data, size_t size) noexcept : data_{data}, size_{size} {}- [[nodiscard]] constexpr T* data() const noexcept { return data_; }- [[nodiscard]] constexpr size_t size() const noexcept { return size_; }- [[nodiscard]] constexpr T* begin() const noexcept { return data_; }- [[nodiscard]] constexpr T* end() const noexcept { return data_ + size_; }- const T& operator[](size_t index) const noexcept { return *(data_ + index); }-- private:- T* data_;- size_t size_;-};-#endif // __cpp_lib_span--struct RefHolder {- RefHolder(std::nullptr_t = nullptr) noexcept {}- explicit RefHolder(napi_env env, napi_value value) : env_(env) {- // Start with 2 to avoid ever going to 0 that creates a weak ref.- napi_create_reference(env, value, 2, &ref_);- }-- // The class is movable.- RefHolder(RefHolder&& other) noexcept- : env_(std::exchange(other.env_, nullptr)),- ref_(std::exchange(other.ref_, nullptr)) {}-- RefHolder& operator=(RefHolder&& other) noexcept {- if (this != &other) {- swap(*this, other);- RefHolder temp(std::move(other));- }- return *this;- }-- // The class is not copyable.- RefHolder(const RefHolder& other) = delete;- RefHolder& operator=(const RefHolder& other) = delete;-- ~RefHolder() noexcept {- if (env_ != nullptr && ref_ != nullptr) {- uint32_t refCount{};- napi_reference_unref(env_, ref_, &refCount);- if (refCount == 1) {- napi_delete_reference(env_, ref_);- }- }- }-- operator napi_value() const {- napi_value result{};- if (ref_ != nullptr) {- napi_get_reference_value(env_, ref_, &result);- }- return result;- }-- explicit operator bool() const noexcept { return ref_ != nullptr; }-- friend void swap(RefHolder& left, RefHolder& right) noexcept {- using std::swap;- swap(left.env_, right.env_);- swap(left.ref_, right.ref_);- }-- private:- napi_env env_{};- napi_ref ref_{};-};
object-template-demo/napi/object-template-demo.cc
@@ -1,306 +0,0 @@-#include <memory>-#include <cstring>-#include <string>-#include "proxy-template.h"--struct InstanceData {- RefHolder constructor_;- std::unique_ptr<ProxyTemplate> proxyTemplate_;-};--class NamedInterceptor {- char buf[256];-- public:- NamedInterceptor() { std::strncpy(this->buf, "foo", sizeof(this->buf)); }- static napi_value Init(napi_env env, napi_value exports);- static napi_value Constructor(napi_env env);- static napi_value NewInstance(napi_env env);- static napi_value New(napi_env env, napi_callback_info info);- static napi_value CreateNew(napi_env env, napi_callback_info info);-- static napi_status ToUtf8String(napi_env env,- napi_value value,- std::string* result);- static napi_status PropertyGetter(napi_env env,- napi_value target,- napi_value key,- napi_value receiver,- napi_value* result);- static napi_status PropertySetter(napi_env env,- napi_value target,- napi_value key,- napi_value value,- napi_value receiver,- bool* result);- static napi_status PropertyDeleter(napi_env env,- napi_value target,- napi_value key,- bool* result);- static napi_status PropertyQuery(napi_env env,- napi_value target,- napi_value key,- bool* result);- static napi_status GetOwnPropertyDescriptor(napi_env env,- napi_value target,- napi_value key,- napi_value* result);- static napi_status OwnKeys(napi_env env,- napi_value target,- napi_value* result);-};--napi_value NamedInterceptor::CreateNew(napi_env env, napi_callback_info info) {- return NamedInterceptor::NewInstance(env);-}--napi_value NamedInterceptor::Init(napi_env env, napi_value exports) {- napi_value constructor{};- NAPI_CALL(napi_define_class(env,- "NamedInterceptor",- NAPI_AUTO_LENGTH,- NamedInterceptor::New,- nullptr,- 0,- nullptr,- &constructor));-- ProxyHandlerBuilder handlerBuilder{};- NAPI_CALL(handlerBuilder.Get(env, &PropertyGetter));- NAPI_CALL(handlerBuilder.Set(env, &PropertySetter));- NAPI_CALL(handlerBuilder.DeleteProperty(env, &PropertyDeleter));- NAPI_CALL(handlerBuilder.Has(env, &PropertyQuery));- NAPI_CALL(- handlerBuilder.GetOwnPropertyDescriptor(env, &GetOwnPropertyDescriptor));- NAPI_CALL(handlerBuilder.OwnKeys(env, &OwnKeys));- napi_value proxyHandler{};- NAPI_CALL(handlerBuilder.NewHandler(env, &proxyHandler));- NAPI_CALL(napi_set_instance_data(- env,- new InstanceData{RefHolder(env, constructor),- std::make_unique<ProxyTemplate>(env, proxyHandler)},- [](napi_env /*env*/, void* data, void* /*hint*/) {- delete static_cast<InstanceData*>(data);- },- nullptr));-- napi_value createFunc{};- NAPI_CALL(napi_create_function(- env, "create", NAPI_AUTO_LENGTH, CreateNew, nullptr, &createFunc));-- NAPI_CALL(napi_set_named_property(env, exports, "create", createFunc));- return exports;-}--napi_value NamedInterceptor::Constructor(napi_env env) {- InstanceData* instanceData{};- NAPI_CALL(- napi_get_instance_data(env, reinterpret_cast<void**>(&instanceData)));- return instanceData->constructor_;-}--napi_value NamedInterceptor::NewInstance(napi_env env) {- napi_escapable_handle_scope scope{};- NAPI_CALL(napi_open_escapable_handle_scope(env, &scope));-- napi_value instance{};- NAPI_CALL(napi_new_instance(env, Constructor(env), 0, nullptr, &instance));-- napi_value result{};- NAPI_CALL(napi_escape_handle(env, scope, instance, &result));- NAPI_CALL(napi_close_escapable_handle_scope(env, scope));- return result;-}--napi_value NamedInterceptor::New(napi_env env, napi_callback_info info) {- napi_value newTarget{};- NAPI_CALL(napi_get_new_target(env, info, &newTarget));- NODE_API_ASSERT_BASE(- env, newTarget != nullptr, "Must be invoked as a constructor.", nullptr);-- napi_value proxyTarget{}, result{};- NAPI_CALL(napi_create_function(- env,- "target",- NAPI_AUTO_LENGTH,- [](napi_env, napi_callback_info) -> napi_value { return nullptr; },- nullptr,- &proxyTarget));-- NamedInterceptor* obj = new NamedInterceptor();-- NAPI_CALL(napi_wrap(- env,- proxyTarget,- obj,- [](napi_env /*env*/, void* data, void* /*hint*/) {- delete reinterpret_cast<NamedInterceptor*>(data);- },- nullptr,- nullptr));-- InstanceData* instanceData{};- NAPI_CALL(- napi_get_instance_data(env, reinterpret_cast<void**>(&instanceData)));- NAPI_CALL(- instanceData->proxyTemplate_->NewInstance(env, proxyTarget, &result));- return result;-}--napi_status NamedInterceptor::ToUtf8String(napi_env env,- napi_value value,- std::string* result) {- size_t size{};- CHECK_NAPI(napi_get_value_string_utf8(env, value, nullptr, 0, &size));- result->assign(size, ' ');- return napi_get_value_string_utf8(- env, value, &(*result)[0], size + 1, nullptr);-}--napi_status NamedInterceptor::PropertyGetter(napi_env env,- napi_value target,- napi_value key,- napi_value receiver,- napi_value* result) {- NamedInterceptor* interceptor{};- CHECK_NAPI(napi_unwrap(env, target, reinterpret_cast<void**>(&interceptor)));- std::string keyStr;- CHECK_NAPI(ToUtf8String(env, key, &keyStr));- if (keyStr == "prop") {- return napi_create_string_utf8(- env, interceptor->buf, NAPI_AUTO_LENGTH, result);- } else {- return napi_create_string_utf8(env, "bar", NAPI_AUTO_LENGTH, result);- }-}--napi_status NamedInterceptor::PropertySetter(napi_env env,- napi_value target,- napi_value key,- napi_value value,- napi_value receiver,- bool* result) {- NamedInterceptor* interceptor{};- CHECK_NAPI(napi_unwrap(env, target, reinterpret_cast<void**>(&interceptor)));- std::string keyStr;- CHECK_NAPI(ToUtf8String(env, key, &keyStr));- if (keyStr == "prop") {- std::string valueStr;- CHECK_NAPI(ToUtf8String(env, value, &valueStr));- std::strncpy(interceptor->buf, valueStr.data(), sizeof(interceptor->buf));- *result = true;- } else {- *result = false;- }- return napi_ok;-}--napi_status NamedInterceptor::PropertyDeleter(napi_env env,- napi_value target,- napi_value key,- bool* result) {- NamedInterceptor* interceptor{};- CHECK_NAPI(napi_unwrap(env, target, reinterpret_cast<void**>(&interceptor)));- std::strncpy(interceptor->buf, "goober", sizeof(interceptor->buf));- *result = true;- return napi_ok;-}--napi_status NamedInterceptor::PropertyQuery(napi_env env,- napi_value target,- napi_value key,- bool* result) {- std::string keyStr;- CHECK_NAPI(ToUtf8String(env, key, &keyStr));-- if (keyStr == "thing") {- *result = true;- }- if (keyStr == "value") {- *result = true;- }- return napi_ok;-}--napi_status NamedInterceptor::GetOwnPropertyDescriptor(napi_env env,- napi_value target,- napi_value key,- napi_value* result) {- std::string keyStr;- CHECK_NAPI(ToUtf8String(env, key, &keyStr));-- if (keyStr == "thing") {- CHECK_NAPI(napi_create_object(env, result));- napi_value trueValue{};- CHECK_NAPI(napi_get_boolean(env, true, &trueValue));- CHECK_NAPI(- napi_set_named_property(env, *result, "configurable", trueValue));- CHECK_NAPI(napi_set_named_property(env, *result, "writable", trueValue));- }- if (keyStr == "value") {- CHECK_NAPI(napi_create_object(env, result));- napi_value trueValue{};- CHECK_NAPI(napi_get_boolean(env, true, &trueValue));- CHECK_NAPI(- napi_set_named_property(env, *result, "configurable", trueValue));- CHECK_NAPI(napi_set_named_property(env, *result, "writable", trueValue));- CHECK_NAPI(napi_set_named_property(env, *result, "enumerable", trueValue));- }- if (keyStr == "arguments" || keyStr == "caller") {- CHECK_NAPI(napi_create_object(env, result));- napi_value trueValue{}, falseValue{};- CHECK_NAPI(napi_get_boolean(env, false, &falseValue));- CHECK_NAPI(napi_get_boolean(env, false, &trueValue));- CHECK_NAPI(- napi_set_named_property(env, *result, "configurable", falseValue));- CHECK_NAPI(napi_set_named_property(env, *result, "writable", falseValue));- CHECK_NAPI(napi_set_named_property(env, *result, "enumerable", falseValue));- napi_value nullValue{};- CHECK_NAPI(napi_get_null(env, &nullValue));- CHECK_NAPI(napi_set_named_property(env, *result, "value", nullValue));- }- if (keyStr == "prototype") {- CHECK_NAPI(napi_create_object(env, result));- napi_value trueValue{}, falseValue{};- CHECK_NAPI(napi_get_boolean(env, false, &falseValue));- CHECK_NAPI(napi_get_boolean(env, true, &trueValue));- CHECK_NAPI(- napi_set_named_property(env, *result, "configurable", falseValue));- CHECK_NAPI(napi_set_named_property(env, *result, "writable", trueValue));- CHECK_NAPI(napi_set_named_property(env, *result, "enumerable", falseValue));- napi_value nullValue{};- CHECK_NAPI(napi_get_null(env, &nullValue));- CHECK_NAPI(napi_set_named_property(env, *result, "value", nullValue));- }- return napi_ok;-}--napi_status NamedInterceptor::OwnKeys(napi_env env,- napi_value target,- napi_value* result) {- CHECK_NAPI(napi_get_all_property_names(env,- target,- napi_key_own_only,- napi_key_all_properties,- napi_key_keep_numbers,- result));-- uint32_t arraySize{};- CHECK_NAPI(napi_get_array_length(env, *result, &arraySize));- napi_value firstElement{};- CHECK_NAPI(napi_get_element(env, *result, 0, &firstElement));- CHECK_NAPI(napi_set_element(env, *result, arraySize, firstElement));- napi_value arraySizeValue{};- CHECK_NAPI(napi_create_uint32(env, arraySize, &arraySizeValue));- CHECK_NAPI(napi_set_named_property(env, *result, "length", arraySizeValue));- napi_value value{};- CHECK_NAPI(napi_create_string_utf8(env, "value", NAPI_AUTO_LENGTH, &value));- CHECK_NAPI(napi_set_element(env, *result, 0, value));- return napi_ok;-}--extern "C" napi_value Init(napi_env env, napi_value exports) {- return NamedInterceptor::Init(env, exports);-}--NAPI_MODULE(NODE_GYP_MODULE_NAME, Init);
object-template-demo/napi/package.json
@@ -1,14 +0,0 @@-{- "name": "object_template_demo",- "version": "0.0.0",- "description": "Intercept named property access using Proxy",- "main": "index.js",- "private": true,- "gypfile": true,- "scripts": {- "test": "node index.js"- },- "dependencies": {- "bindings": "~1.5.0"- }-}
object-template-demo/napi/proxy-template.cc
@@ -1,726 +0,0 @@-#include "proxy-template.h"-#include <vector>--namespace {--static napi_status SetTrap(napi_env env,- napi_value handler,- const char* propertyName,- napi_value trap) noexcept {- if (trap != nullptr) {- CHECK_NAPI(napi_set_named_property(env, handler, propertyName, trap));- }- return napi_ok;-}--using TrapCaller = napi_status (*)(napi_env env,- void* trap,- napi_span<napi_value> args,- napi_value* result);--struct TrapCallInfo {- TrapCaller Caller;- void* trap;-};--template <size_t argCount>-napi_callback CreateTrapCallback() noexcept {- return [](napi_env env, napi_callback_info info) noexcept -> napi_value {- TrapCallInfo* trapCallInfo{};- napi_value args[argCount]{};- size_t actualArgCount{argCount};- NAPI_CALL(napi_get_cb_info(env,- info,- &actualArgCount,- args,- nullptr,- reinterpret_cast<void**>(&trapCallInfo)));- NODE_API_ASSERT_BASE(env,- actualArgCount == argCount,- "proxy trap requires argCount arguments.",- nullptr);- napi_value result{};- NAPI_CALL(trapCallInfo->Caller(- env, trapCallInfo->trap, span<napi_value>(args, argCount), &result));- return result;- };-}--napi_status ToVector(napi_env env,- napi_value arrayValue,- std::vector<napi_value>* result) {- bool isArray{};- CHECK_NAPI(napi_is_array(env, arrayValue, &isArray));- if (isArray) {- uint32_t arraySize{};- CHECK_NAPI(napi_get_array_length(env, arrayValue, &arraySize));- result->reserve(result->size() + arraySize);- for (uint32_t i = 0; i < arraySize; ++i) {- napi_value arrayElement{};- CHECK_NAPI(napi_get_element(env, arrayValue, i, &arrayElement));- result->push_back(arrayElement);- }- }- return napi_ok;-}--} // namespace--napi_status ProxyHandlerBuilder::NewHandler(napi_env env,- napi_value* result) noexcept {- CHECK_NAPI(napi_create_object(env, result));-- CHECK_NAPI(SetTrap(env, *result, "apply", apply_));- CHECK_NAPI(SetTrap(env, *result, "construct", construct_));- CHECK_NAPI(SetTrap(env, *result, "defineProperty", defineProperty_));- CHECK_NAPI(SetTrap(env, *result, "deleteProperty", deleteProperty_));- CHECK_NAPI(SetTrap(env, *result, "get", get_));- CHECK_NAPI(SetTrap(- env, *result, "getOwnPropertyDescriptor", getOwnPropertyDescriptor_));- CHECK_NAPI(SetTrap(env, *result, "getPrototypeOf", getPrototypeOf_));- CHECK_NAPI(SetTrap(env, *result, "has", has_));- CHECK_NAPI(SetTrap(env, *result, "isExtensible", isExtensible_));- CHECK_NAPI(SetTrap(env, *result, "ownKeys", ownKeys_));- CHECK_NAPI(SetTrap(env, *result, "preventExtensions", preventExtensions_));- CHECK_NAPI(SetTrap(env, *result, "set", set_));- CHECK_NAPI(SetTrap(env, *result, "setPrototypeOf", setPrototypeOf_));- return napi_ok;-}--napi_value ProxyHandlerBuilder::Apply() noexcept {- return apply_;-}--void ProxyHandlerBuilder::Apply(napi_value trap) noexcept {- apply_ = trap;-}--napi_status ProxyHandlerBuilder::Apply(napi_env env,- napi_callback trap,- void* data) noexcept {- return napi_create_function(- env, "apply", NAPI_AUTO_LENGTH, trap, data, &apply_);-}--napi_status ProxyHandlerBuilder::Apply(- napi_env env,- napi_status (*trap)(napi_env env,- napi_value target,- napi_value thisArg,- napi_value argumentList,- napi_value* result)) {- using TrapType = decltype(trap);- static TrapCallInfo callInfo{[](napi_env env,- void* trap,- napi_span<napi_value> args,- napi_value* result) noexcept {- return reinterpret_cast<TrapType>(trap)(- env, args[0], args[1], args[2], result);- },- reinterpret_cast<void*>(trap)};- return Apply(env, CreateTrapCallback<3>(), &callInfo);-}--napi_status ProxyHandlerBuilder::Apply(- napi_env env,- napi_status (*trap)(napi_env env,- napi_value target,- napi_value thisArg,- napi_span<napi_value> argumentList,- napi_value* result)) {- using TrapType = decltype(trap);- static TrapCallInfo callInfo{- [](napi_env env,- void* trap,- napi_span<napi_value> args,- napi_value* result) noexcept {- std::vector<napi_value> argVector;- CHECK_NAPI(ToVector(env, args[2], &argVector));- return reinterpret_cast<TrapType>(trap)(- env,- args[0],- args[1],- span<napi_value>(argVector.data(), argVector.size()),- result);- },- reinterpret_cast<void*>(trap)};- return Apply(env, CreateTrapCallback<3>(), &callInfo);-}--napi_value ProxyHandlerBuilder::Construct() noexcept {- return construct_;-}--void ProxyHandlerBuilder::Construct(napi_value trap) noexcept {- construct_ = trap;-}--napi_status ProxyHandlerBuilder::Construct(napi_env env,- napi_callback trap,- void* data) noexcept {- return napi_create_function(- env, "construct", NAPI_AUTO_LENGTH, trap, data, &construct_);-}--napi_status ProxyHandlerBuilder::Construct(- napi_env env,- napi_status (*trap)(napi_env env,- napi_value target,- napi_value argumentList,- napi_value newTarget,- napi_value* result)) {- using TrapType = decltype(trap);- static TrapCallInfo callInfo{[](napi_env env,- void* trap,- napi_span<napi_value> args,- napi_value* result) noexcept {- return reinterpret_cast<TrapType>(trap)(- env, args[0], args[1], args[2], result);- },- reinterpret_cast<void*>(trap)};- return Construct(env, CreateTrapCallback<3>(), &callInfo);-}--napi_status ProxyHandlerBuilder::Construct(- napi_env env,- napi_status (*trap)(napi_env env,- napi_value target,- napi_span<napi_value> argumentList,- napi_value newTarget,- napi_value* result)) {- using TrapType = decltype(trap);- static TrapCallInfo callInfo{- [](napi_env env,- void* trap,- napi_span<napi_value> args,- napi_value* result) noexcept {- std::vector<napi_value> argVector;- CHECK_NAPI(ToVector(env, args[1], &argVector));- return reinterpret_cast<TrapType>(trap)(- env,- args[0],- span<napi_value>(argVector.data(), argVector.size()),- args[2],- result);- },- reinterpret_cast<void*>(trap)};- return Construct(env, CreateTrapCallback<3>(), &callInfo);-}--napi_value ProxyHandlerBuilder::DefineProperty() noexcept {- return defineProperty_;-}--void ProxyHandlerBuilder::DefineProperty(napi_value trap) noexcept {- defineProperty_ = trap;-}--napi_status ProxyHandlerBuilder::DefineProperty(napi_env env,- napi_callback trap,- void* data) noexcept {- return napi_create_function(- env, "defineProperty", NAPI_AUTO_LENGTH, trap, data, &defineProperty_);-}--napi_status ProxyHandlerBuilder::DefineProperty(- napi_env env,- napi_status (*trap)(napi_env env,- napi_value target,- napi_value key,- napi_value descriptor,- napi_value* result)) {- using TrapType = decltype(trap);- static TrapCallInfo callInfo{[](napi_env env,- void* trap,- napi_span<napi_value> args,- napi_value* result) noexcept {- return reinterpret_cast<TrapType>(trap)(- env, args[0], args[1], args[2], result);- },- reinterpret_cast<void*>(trap)};- return DefineProperty(env, CreateTrapCallback<3>(), &callInfo);-}--napi_status ProxyHandlerBuilder::DefineProperty(- napi_env env,- napi_status (*trap)(napi_env env,- napi_value target,- napi_value key,- napi_value descriptor,- bool* result)) {- using TrapType = decltype(trap);- static TrapCallInfo callInfo{- [](napi_env env,- void* trap,- napi_span<napi_value> args,- napi_value* result) noexcept {- bool boolResult{};- CHECK_NAPI(reinterpret_cast<TrapType>(trap)(- env, args[0], args[1], args[2], &boolResult));- return napi_get_boolean(env, boolResult, result);- },- reinterpret_cast<void*>(trap)};- return DefineProperty(env, CreateTrapCallback<3>(), &callInfo);-}--napi_value ProxyHandlerBuilder::DeleteProperty() noexcept {- return deleteProperty_;-}--void ProxyHandlerBuilder::DeleteProperty(napi_value trap) noexcept {- deleteProperty_ = trap;-}--napi_status ProxyHandlerBuilder::DeleteProperty(napi_env env,- napi_callback trap,- void* data) noexcept {- return napi_create_function(- env, "deleteProperty", NAPI_AUTO_LENGTH, trap, data, &deleteProperty_);-}--napi_status ProxyHandlerBuilder::DeleteProperty(- napi_env env,- napi_status (*trap)(- napi_env env, napi_value target, napi_value key, napi_value* result)) {- using TrapType = decltype(trap);- static TrapCallInfo callInfo{[](napi_env env,- void* trap,- napi_span<napi_value> args,- napi_value* result) noexcept {- return reinterpret_cast<TrapType>(trap)(- env, args[0], args[1], result);- },- reinterpret_cast<void*>(trap)};- return DeleteProperty(env, CreateTrapCallback<2>(), &callInfo);-}--napi_status ProxyHandlerBuilder::DeleteProperty(- napi_env env,- napi_status (*trap)(- napi_env env, napi_value target, napi_value key, bool* result)) {- using TrapType = decltype(trap);- static TrapCallInfo callInfo{[](napi_env env,- void* trap,- napi_span<napi_value> args,- napi_value* result) noexcept {- bool boolResult{};- CHECK_NAPI(reinterpret_cast<TrapType>(trap)(- env, args[0], args[1], &boolResult));- return napi_get_boolean(- env, boolResult, result);- },- reinterpret_cast<void*>(trap)};- return DeleteProperty(env, CreateTrapCallback<2>(), &callInfo);-}--napi_value ProxyHandlerBuilder::Get() noexcept {- return get_;-}--void ProxyHandlerBuilder::Get(napi_value trap) noexcept {- get_ = trap;-}--napi_status ProxyHandlerBuilder::Get(napi_env env,- napi_callback trap,- void* data) noexcept {- return napi_create_function(env, "get", NAPI_AUTO_LENGTH, trap, data, &get_);-}--napi_status ProxyHandlerBuilder::Get(napi_env env,- napi_status (*trap)(napi_env env,- napi_value target,- napi_value key,- napi_value receiver,- napi_value* result)) {- using TrapType = decltype(trap);- static TrapCallInfo callInfo{[](napi_env env,- void* trap,- napi_span<napi_value> args,- napi_value* result) noexcept {- return reinterpret_cast<TrapType>(trap)(- env, args[0], args[1], args[2], result);- },- reinterpret_cast<void*>(trap)};- return Get(env, CreateTrapCallback<3>(), &callInfo);-}--napi_value ProxyHandlerBuilder::GetOwnPropertyDescriptor() noexcept {- return getOwnPropertyDescriptor_;-}--void ProxyHandlerBuilder::GetOwnPropertyDescriptor(napi_value trap) noexcept {- getOwnPropertyDescriptor_ = trap;-}--napi_status ProxyHandlerBuilder::GetOwnPropertyDescriptor(napi_env env,- napi_callback trap,- void* data) noexcept {- return napi_create_function(env,- "getOwnPropertyDescriptor",- NAPI_AUTO_LENGTH,- trap,- data,- &getOwnPropertyDescriptor_);-}--napi_status ProxyHandlerBuilder::GetOwnPropertyDescriptor(- napi_env env,- napi_status (*trap)(- napi_env env, napi_value target, napi_value key, napi_value* result)) {- using TrapType = decltype(trap);- static TrapCallInfo callInfo{[](napi_env env,- void* trap,- napi_span<napi_value> args,- napi_value* result) noexcept {- return reinterpret_cast<TrapType>(trap)(- env, args[0], args[1], result);- },- reinterpret_cast<void*>(trap)};- return GetOwnPropertyDescriptor(env, CreateTrapCallback<2>(), &callInfo);-}--napi_value ProxyHandlerBuilder::GetPrototypeOf() noexcept {- return getPrototypeOf_;-}--void ProxyHandlerBuilder::GetPrototypeOf(napi_value trap) noexcept {- getPrototypeOf_ = trap;-}--napi_status ProxyHandlerBuilder::GetPrototypeOf(napi_env env,- napi_callback trap,- void* data) noexcept {- return napi_create_function(- env, "getPrototypeOf", NAPI_AUTO_LENGTH, trap, data, &getPrototypeOf_);-}--napi_status ProxyHandlerBuilder::GetPrototypeOf(- napi_env env,- napi_status (*trap)(napi_env env, napi_value target, napi_value* result)) {- using TrapType = decltype(trap);- static TrapCallInfo callInfo{[](napi_env env,- void* trap,- napi_span<napi_value> args,- napi_value* result) noexcept {- return reinterpret_cast<TrapType>(trap)(- env, args[0], result);- },- reinterpret_cast<void*>(trap)};- return GetPrototypeOf(env, CreateTrapCallback<1>(), &callInfo);-}--napi_value ProxyHandlerBuilder::Has() noexcept {- return has_;-}--void ProxyHandlerBuilder::Has(napi_value trap) noexcept {- has_ = trap;-}--napi_status ProxyHandlerBuilder::Has(napi_env env,- napi_callback trap,- void* data) noexcept {- return napi_create_function(env, "has", NAPI_AUTO_LENGTH, trap, data, &has_);-}--napi_status ProxyHandlerBuilder::Has(napi_env env,- napi_status (*trap)(napi_env env,- napi_value target,- napi_value key,- napi_value* result)) {- using TrapType = decltype(trap);- static TrapCallInfo callInfo{[](napi_env env,- void* trap,- napi_span<napi_value> args,- napi_value* result) noexcept {- return reinterpret_cast<TrapType>(trap)(- env, args[0], args[1], result);- },- reinterpret_cast<void*>(trap)};- return Has(env, CreateTrapCallback<2>(), &callInfo);-}--napi_status ProxyHandlerBuilder::Has(napi_env env,- napi_status (*trap)(napi_env env,- napi_value target,- napi_value key,- bool* result)) {- using TrapType = decltype(trap);- static TrapCallInfo callInfo{[](napi_env env,- void* trap,- napi_span<napi_value> args,- napi_value* result) noexcept {- bool boolResult{};- CHECK_NAPI(reinterpret_cast<TrapType>(trap)(- env, args[0], args[1], &boolResult));- return napi_get_boolean(- env, boolResult, result);- },- reinterpret_cast<void*>(trap)};- return Has(env, CreateTrapCallback<2>(), &callInfo);-}--napi_value ProxyHandlerBuilder::IsExtensible() noexcept {- return isExtensible_;-}--void ProxyHandlerBuilder::IsExtensible(napi_value trap) noexcept {- isExtensible_ = trap;-}--napi_status ProxyHandlerBuilder::IsExtensible(napi_env env,- napi_callback trap,- void* data) noexcept {- return napi_create_function(- env, "isExtensible", NAPI_AUTO_LENGTH, trap, data, &isExtensible_);-}--napi_status ProxyHandlerBuilder::IsExtensible(- napi_env env,- napi_status (*trap)(napi_env env, napi_value target, napi_value* result)) {- using TrapType = decltype(trap);- static TrapCallInfo callInfo{[](napi_env env,- void* trap,- napi_span<napi_value> args,- napi_value* result) noexcept {- return reinterpret_cast<TrapType>(trap)(- env, args[0], result);- },- reinterpret_cast<void*>(trap)};- return IsExtensible(env, CreateTrapCallback<1>(), &callInfo);-}--napi_status ProxyHandlerBuilder::IsExtensible(- napi_env env,- napi_status (*trap)(napi_env env, napi_value target, bool* result)) {- using TrapType = decltype(trap);- static TrapCallInfo callInfo{- [](napi_env env,- void* trap,- napi_span<napi_value> args,- napi_value* result) noexcept {- bool boolResult{};- CHECK_NAPI(reinterpret_cast<TrapType>(trap)(env, args[0], &boolResult));- return napi_get_boolean(env, boolResult, result);- },- reinterpret_cast<void*>(trap)};- return IsExtensible(env, CreateTrapCallback<1>(), &callInfo);-}--napi_value ProxyHandlerBuilder::OwnKeys() noexcept {- return ownKeys_;-}--void ProxyHandlerBuilder::OwnKeys(napi_value trap) noexcept {- ownKeys_ = trap;-}--napi_status ProxyHandlerBuilder::OwnKeys(napi_env env,- napi_callback trap,- void* data) noexcept {- return napi_create_function(- env, "ownKeys", NAPI_AUTO_LENGTH, trap, data, &ownKeys_);-}--napi_status ProxyHandlerBuilder::OwnKeys(- napi_env env,- napi_status (*trap)(napi_env env, napi_value target, napi_value* result)) {- using TrapType = decltype(trap);- static TrapCallInfo callInfo{[](napi_env env,- void* trap,- napi_span<napi_value> args,- napi_value* result) noexcept {- return reinterpret_cast<TrapType>(trap)(- env, args[0], result);- },- reinterpret_cast<void*>(trap)};- return OwnKeys(env, CreateTrapCallback<1>(), &callInfo);-}--napi_value ProxyHandlerBuilder::PreventExtensions() noexcept {- return preventExtensions_;-}--void ProxyHandlerBuilder::PreventExtensions(napi_value trap) noexcept {- preventExtensions_ = trap;-}--napi_status ProxyHandlerBuilder::PreventExtensions(napi_env env,- napi_callback trap,- void* data) noexcept {- return napi_create_function(env,- "preventExtensions",- NAPI_AUTO_LENGTH,- trap,- data,- &preventExtensions_);-}--napi_status ProxyHandlerBuilder::PreventExtensions(- napi_env env,- napi_status (*trap)(napi_env env, napi_value target, napi_value* result)) {- using TrapType = decltype(trap);- static TrapCallInfo callInfo{[](napi_env env,- void* trap,- napi_span<napi_value> args,- napi_value* result) noexcept {- return reinterpret_cast<TrapType>(trap)(- env, args[0], result);- },- reinterpret_cast<void*>(trap)};- return PreventExtensions(env, CreateTrapCallback<1>(), &callInfo);-}--napi_status ProxyHandlerBuilder::PreventExtensions(- napi_env env,- napi_status (*trap)(napi_env env, napi_value target, bool* result)) {- using TrapType = decltype(trap);- static TrapCallInfo callInfo{- [](napi_env env,- void* trap,- napi_span<napi_value> args,- napi_value* result) noexcept {- bool boolResult{};- CHECK_NAPI(reinterpret_cast<TrapType>(trap)(env, args[0], &boolResult));- return napi_get_boolean(env, boolResult, result);- },- reinterpret_cast<void*>(trap)};- return PreventExtensions(env, CreateTrapCallback<1>(), &callInfo);-}--napi_value ProxyHandlerBuilder::Set() noexcept {- return set_;-}--void ProxyHandlerBuilder::Set(napi_value trap) noexcept {- set_ = trap;-}--napi_status ProxyHandlerBuilder::Set(napi_env env,- napi_callback trap,- void* data) noexcept {- return napi_create_function(env, "set", NAPI_AUTO_LENGTH, trap, data, &set_);-}--napi_status ProxyHandlerBuilder::Set(napi_env env,- napi_status (*trap)(napi_env env,- napi_value target,- napi_value key,- napi_value value,- napi_value receiver,- napi_value* result)) {- using TrapType = decltype(trap);- static TrapCallInfo callInfo{- [](napi_env env,- void* trap,- napi_span<napi_value> args,- napi_value* result) noexcept {- return reinterpret_cast<TrapType>(trap)(- env, args[0], args[1], args[2], args[3], result);- },- reinterpret_cast<void*>(trap)};- return Set(env, CreateTrapCallback<4>(), &callInfo);-}--napi_status ProxyHandlerBuilder::Set(napi_env env,- napi_status (*trap)(napi_env env,- napi_value target,- napi_value key,- napi_value value,- napi_value receiver,- bool* result)) {- using TrapType = decltype(trap);- static TrapCallInfo callInfo{- [](napi_env env,- void* trap,- napi_span<napi_value> args,- napi_value* result) noexcept {- bool boolResult{};- CHECK_NAPI(reinterpret_cast<TrapType>(trap)(- env, args[0], args[1], args[2], args[3], &boolResult));- return napi_get_boolean(env, boolResult, result);- },- reinterpret_cast<void*>(trap)};- return Set(env, CreateTrapCallback<4>(), &callInfo);-}--napi_value ProxyHandlerBuilder::SetPrototypeOf() noexcept {- return setPrototypeOf_;-}--void ProxyHandlerBuilder::SetPrototypeOf(napi_value trap) noexcept {- setPrototypeOf_ = trap;-}--napi_status ProxyHandlerBuilder::SetPrototypeOf(napi_env env,- napi_callback trap,- void* data) noexcept {- return napi_create_function(- env, "setPrototypeOf", NAPI_AUTO_LENGTH, trap, data, &setPrototypeOf_);-}--napi_status ProxyHandlerBuilder::SetPrototypeOf(- napi_env env,- napi_status (*trap)(napi_env env,- napi_value target,- napi_value prototype,- napi_value* result)) {- using TrapType = decltype(trap);- static TrapCallInfo callInfo{[](napi_env env,- void* trap,- napi_span<napi_value> args,- napi_value* result) noexcept {- return reinterpret_cast<TrapType>(trap)(- env, args[0], args[1], result);- },- reinterpret_cast<void*>(trap)};- return SetPrototypeOf(env, CreateTrapCallback<2>(), &callInfo);-}--napi_status ProxyHandlerBuilder::SetPrototypeOf(- napi_env env,- napi_status (*trap)(- napi_env env, napi_value target, napi_value prototype, bool* result)) {- using TrapType = decltype(trap);- static TrapCallInfo callInfo{[](napi_env env,- void* trap,- napi_span<napi_value> args,- napi_value* result) noexcept {- bool boolResult{};- CHECK_NAPI(reinterpret_cast<TrapType>(trap)(- env, args[0], args[1], &boolResult));- return napi_get_boolean(- env, boolResult, result);- },- reinterpret_cast<void*>(trap)};- return SetPrototypeOf(env, CreateTrapCallback<2>(), &callInfo);-}--//=============--ProxyTemplate::ProxyTemplate(napi_env env, napi_value proxyHandler) noexcept- : proxyHandler_(RefHolder(env, proxyHandler)) {}--napi_status ProxyTemplate::NewInstance(napi_env env,- napi_value target,- napi_value* result) noexcept {- napi_value proxyConstructor{};- CHECK_NAPI(GetProxyConstructor(env, &proxyConstructor));-- napi_value args[] = {target, static_cast<napi_value>(proxyHandler_)};- return napi_new_instance(env, proxyConstructor, 2, args, result);-}--napi_status ProxyTemplate::GetProxyConstructor(napi_env env,- napi_value* result) noexcept {- if (!proxyConstructor_) {- napi_value global{}, proxyConstructor{};- CHECK_NAPI(napi_get_global(env, &global));- CHECK_NAPI(- napi_get_named_property(env, global, "Proxy", &proxyConstructor));- proxyConstructor_ = RefHolder(env, proxyConstructor);- }- *result = proxyConstructor_;- return napi_ok;-}
object-template-demo/napi/proxy-template.h
@@ -1,249 +0,0 @@-#include "node-api-common.h"--template <typename T>-using napi_span = span<T>;--// Creates a handler object for a JavaScript Proxy.-// See-// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy-// for details about JavaScript Proxy.-//-// There are four different ways we can set each Proxy handler trap:-// - using napi_value function.-// - using napi_callback that creates function.-// - using a callback signature that matches Proxy handler definition arguments.-// - using a callback based on the Proxy handler trap signature where we replace-// napi_value with strongly typed values such as bool or-// napi_span<napi_value>.-//-// Note that ProxyHandlerBuilder keeps handler trap functions as napi_value-// and thus cannot be persisted. The typical usage is to create new-// ProxyHandlerBuilder instance on the call stack, set handler traps, create-// handler to pass to Proxy constructor or ProxyTemplate, and not to reuse the-// ProxyHandlerBuilder instance.-struct ProxyHandlerBuilder {- napi_status NewHandler(napi_env env, napi_value* result) noexcept;-- napi_value Apply() noexcept;- void Apply(napi_value trap) noexcept;- napi_status Apply(napi_env env,- napi_callback trap,- void* data = nullptr) noexcept;- napi_status Apply(napi_env env,- napi_status (*trap)(napi_env env,- napi_value target,- napi_value thisArg,- napi_value argumentList,- napi_value* result));- napi_status Apply(napi_env env,- napi_status (*trap)(napi_env env,- napi_value target,- napi_value thisArg,- napi_span<napi_value> argumentList,- napi_value* result));-- napi_value Construct() noexcept;- void Construct(napi_value trap) noexcept;- napi_status Construct(napi_env env,- napi_callback trap,- void* data = nullptr) noexcept;- napi_status Construct(napi_env env,- napi_status (*trap)(napi_env env,- napi_value target,- napi_value argumentList,- napi_value newTarget,- napi_value* result));- napi_status Construct(napi_env env,- napi_status (*trap)(napi_env env,- napi_value target,- napi_span<napi_value> argumentList,- napi_value newTarget,- napi_value* result));-- napi_value DefineProperty() noexcept;- void DefineProperty(napi_value trap) noexcept;- napi_status DefineProperty(napi_env env,- napi_callback trap,- void* data = nullptr) noexcept;- napi_status DefineProperty(napi_env env,- napi_status (*trap)(napi_env env,- napi_value target,- napi_value key,- napi_value descriptor,- napi_value* result));- napi_status DefineProperty(napi_env env,- napi_status (*trap)(napi_env env,- napi_value target,- napi_value key,- napi_value descriptor,- bool* result));-- napi_value DeleteProperty() noexcept;- void DeleteProperty(napi_value trap) noexcept;- napi_status DeleteProperty(napi_env env,- napi_callback trap,- void* data = nullptr) noexcept;- napi_status DeleteProperty(napi_env env,- napi_status (*trap)(napi_env env,- napi_value target,- napi_value key,- napi_value* result));- napi_status DeleteProperty(napi_env env,- napi_status (*trap)(napi_env env,- napi_value target,- napi_value key,- bool* result));-- napi_value Get() noexcept;- void Get(napi_value trap) noexcept;- napi_status Get(napi_env env,- napi_callback trap,- void* data = nullptr) noexcept;- napi_status Get(napi_env env,- napi_status (*trap)(napi_env env,- napi_value target,- napi_value key,- napi_value receiver,- napi_value* result));-- napi_value GetOwnPropertyDescriptor() noexcept;- void GetOwnPropertyDescriptor(napi_value trap) noexcept;- napi_status GetOwnPropertyDescriptor(napi_env env,- napi_callback trap,- void* data = nullptr) noexcept;- napi_status GetOwnPropertyDescriptor(napi_env env,- napi_status (*trap)(napi_env env,- napi_value target,- napi_value key,- napi_value* result));-- napi_value GetPrototypeOf() noexcept;- void GetPrototypeOf(napi_value trap) noexcept;- napi_status GetPrototypeOf(napi_env env,- napi_callback trap,- void* data = nullptr) noexcept;- napi_status GetPrototypeOf(napi_env env,- napi_status (*trap)(napi_env env,- napi_value target,- napi_value* result));-- napi_value Has() noexcept;- void Has(napi_value trap) noexcept;- napi_status Has(napi_env env,- napi_callback trap,- void* data = nullptr) noexcept;- napi_status Has(napi_env env,- napi_status (*trap)(napi_env env,- napi_value target,- napi_value key,- napi_value* result));- napi_status Has(napi_env env,- napi_status (*trap)(napi_env env,- napi_value target,- napi_value key,- bool* result));-- napi_value IsExtensible() noexcept;- void IsExtensible(napi_value trap) noexcept;- napi_status IsExtensible(napi_env env,- napi_callback trap,- void* data = nullptr) noexcept;- napi_status IsExtensible(napi_env env,- napi_status (*trap)(napi_env env,- napi_value target,- napi_value* result));- napi_status IsExtensible(napi_env env,- napi_status (*trap)(napi_env env,- napi_value target,- bool* result));-- napi_value OwnKeys() noexcept;- void OwnKeys(napi_value trap) noexcept;- napi_status OwnKeys(napi_env env,- napi_callback trap,- void* data = nullptr) noexcept;- napi_status OwnKeys(napi_env env,- napi_status (*trap)(napi_env env,- napi_value target,- napi_value* result));-- napi_value PreventExtensions() noexcept;- void PreventExtensions(napi_value trap) noexcept;- napi_status PreventExtensions(napi_env env,- napi_callback trap,- void* data = nullptr) noexcept;- napi_status PreventExtensions(napi_env env,- napi_status (*trap)(napi_env env,- napi_value target,- napi_value* result));- napi_status PreventExtensions(napi_env env,- napi_status (*trap)(napi_env env,- napi_value target,- bool* result));-- napi_value Set() noexcept;- void Set(napi_value trap) noexcept;- napi_status Set(napi_env env,- napi_callback trap,- void* data = nullptr) noexcept;- napi_status Set(napi_env env,- napi_status (*trap)(napi_env env,- napi_value target,- napi_value key,- napi_value value,- napi_value receiver,- napi_value* result));- napi_status Set(napi_env env,- napi_status (*trap)(napi_env env,- napi_value target,- napi_value key,- napi_value value,- napi_value receiver,- bool* result));-- napi_value SetPrototypeOf() noexcept;- void SetPrototypeOf(napi_value trap) noexcept;- napi_status SetPrototypeOf(napi_env env,- napi_callback trap,- void* data = nullptr) noexcept;- napi_status SetPrototypeOf(napi_env env,- napi_status (*trap)(napi_env env,- napi_value target,- napi_value prototype,- napi_value* result));- napi_status SetPrototypeOf(napi_env env,- napi_status (*trap)(napi_env env,- napi_value target,- napi_value prototype,- bool* result));-- private:- napi_value apply_{};- napi_value construct_{};- napi_value defineProperty_{};- napi_value deleteProperty_{};- napi_value get_{};- napi_value getOwnPropertyDescriptor_{};- napi_value getPrototypeOf_{};- napi_value has_{};- napi_value isExtensible_{};- napi_value ownKeys_{};- napi_value preventExtensions_{};- napi_value set_{};- napi_value setPrototypeOf_{};-};--struct ProxyTemplate {- ProxyTemplate(napi_env env, napi_value proxyHandler) noexcept;-- napi_status NewInstance(napi_env env,- napi_value target,- napi_value* result) noexcept;-- private:- napi_status GetProxyConstructor(napi_env env, napi_value* result) noexcept;-- private:- RefHolder proxyConstructor_{};- RefHolder proxyHandler_{};-};
object-wrap-demo/node-addon-api/README.md
@@ -1,14 +0,0 @@-# Node-API Object Wrap Demo--This is an example project that accompanies the Node-API workshop tutorials--A tutorial describing this project can be found at the [Node-API Resource](https://napi.inspiredware.com/getting-started/objectwrap.html).--To build and run this program on your system, clone it to your computer and run these two commands inside your clone:--```-npm install-npm test-```--> You need to have Node 10.5.0 or later installed.
object-wrap-demo/node-addon-api/binding.gyp
@@ -1,20 +0,0 @@-{- 'targets': [- {- 'target_name': 'object-wrap-demo-native',- 'sources': [ 'src/object_wrap_demo.cc' ],- 'include_dirs': ["<!@(node -p \"require('node-addon-api').include\")"],- 'dependencies': ["<!(node -p \"require('node-addon-api').gyp\")"],- 'cflags!': [ '-fno-exceptions' ],- 'cflags_cc!': [ '-fno-exceptions' ],- 'xcode_settings': {- 'GCC_ENABLE_CPP_EXCEPTIONS': 'YES',- 'CLANG_CXX_LIBRARY': 'libc++',- 'MACOSX_DEPLOYMENT_TARGET': '10.7'- },- 'msvs_settings': {- 'VCCLCompilerTool': { 'ExceptionHandling': 1 },- }- }- ]-}
object-wrap-demo/node-addon-api/lib/binding.js
@@ -1,11 +0,0 @@-const addon = require('../build/Release/object-wrap-demo-native');--function ObjectWrapDemo(name) {- this.greet = function(str) {- return _addonInstance.greet(str);- }-- var _addonInstance = new addon.ObjectWrapDemo(name);-}--module.exports = ObjectWrapDemo;
object-wrap-demo/node-addon-api/package.json
@@ -1,16 +0,0 @@-{- "main": "lib/binding.js",- "private": true,- "dependencies": {- "node-addon-api": "^1.1.0"- },- "scripts": {- "test": "node --napi-modules ./test/test_binding.js"- },- "gypfile": true,- "name": "object-wrap-demo",- "version": "1.0.0",- "description": "An object wrapper demo",- "author": "Your name goes here",- "license": "MIT"-}
object-wrap-demo/node-addon-api/src/object_wrap_demo.cc
@@ -1,62 +0,0 @@-#include "object_wrap_demo.h"--using namespace Napi;--ObjectWrapDemo::ObjectWrapDemo(const Napi::CallbackInfo& info)- : ObjectWrap(info) {- Napi::Env env = info.Env();-- if (info.Length() < 1) {- Napi::TypeError::New(env, "Wrong number of arguments")- .ThrowAsJavaScriptException();- return;- }-- if (!info[0].IsString()) {- Napi::TypeError::New(env, "You need to name yourself")- .ThrowAsJavaScriptException();- return;- }-- this->_greeterName = info[0].As<Napi::String>().Utf8Value();-}--Napi::Value ObjectWrapDemo::Greet(const Napi::CallbackInfo& info) {- Napi::Env env = info.Env();-- if (info.Length() < 1) {- Napi::TypeError::New(env, "Wrong number of arguments")- .ThrowAsJavaScriptException();- return env.Null();- }-- if (!info[0].IsString()) {- Napi::TypeError::New(env, "You need to introduce yourself to greet")- .ThrowAsJavaScriptException();- return env.Null();- }-- Napi::String name = info[0].As<Napi::String>();-- printf("Hello %s\n", name.Utf8Value().c_str());- printf("I am %s\n", this->_greeterName.c_str());-- return Napi::String::New(env, this->_greeterName);-}--Napi::Function ObjectWrapDemo::GetClass(Napi::Env env) {- return DefineClass(- env,- "ObjectWrapDemo",- {- ObjectWrapDemo::InstanceMethod("greet", &ObjectWrapDemo::Greet),- });-}--Napi::Object Init(Napi::Env env, Napi::Object exports) {- Napi::String name = Napi::String::New(env, "ObjectWrapDemo");- exports.Set(name, ObjectWrapDemo::GetClass(env));- return exports;-}--NODE_API_MODULE(addon, Init)
object-wrap-demo/node-addon-api/src/object_wrap_demo.h
@@ -1,14 +0,0 @@-#pragma once--#include <napi.h>--class ObjectWrapDemo : public Napi::ObjectWrap<ObjectWrapDemo> {- public:- ObjectWrapDemo(const Napi::CallbackInfo&);- Napi::Value Greet(const Napi::CallbackInfo&);-- static Napi::Function GetClass(Napi::Env);-- private:- std::string _greeterName;-};
object-wrap-demo/node-addon-api/test/test_binding.js
@@ -1,21 +0,0 @@-const ObjectWrapDemo = require("../lib/binding.js");-const assert = require("assert");--assert(ObjectWrapDemo, "The expected module is undefined");--function testBasic()-{- const instance = new ObjectWrapDemo("mr-yeoman");- assert(instance.greet, "The expected method is not defined");- assert.strictEqual(instance.greet("kermit"), "mr-yeoman", "Unexpected value returned");-}--function testInvalidParams()-{- const instance = new ObjectWrapDemo();-}--assert.doesNotThrow(testBasic, undefined, "testBasic threw an expection");-assert.throws(testInvalidParams, undefined, "testInvalidParams didn't throw");--console.log("Tests passed- everything looks OK!");
promise-callback-demo/node-addon-api/README.md
@@ -1,7 +0,0 @@-# Node-API Promise Callback Demo--To build and run this program on your system, clone it to your computer and run these two commands inside your clone:--```-npm install-```
promise-callback-demo/node-addon-api/binding.gyp
@@ -1,20 +0,0 @@-{- 'targets': [- {- 'target_name': 'promise-callback-demo',- 'sources': [ 'src/promise_callback_demo.cc' ],- 'include_dirs': ["<!@(node -p \"require('node-addon-api').include\")"],- 'dependencies': ["<!(node -p \"require('node-addon-api').gyp\")"],- 'cflags!': [ '-fno-exceptions' ],- 'cflags_cc!': [ '-fno-exceptions' ],- 'xcode_settings': {- 'GCC_ENABLE_CPP_EXCEPTIONS': 'YES',- 'CLANG_CXX_LIBRARY': 'libc++',- 'MACOSX_DEPLOYMENT_TARGET': '10.7'- },- 'msvs_settings': {- 'VCCLCompilerTool': { 'ExceptionHandling': 1 },- }- }- ]-}
promise-callback-demo/node-addon-api/package.json
@@ -1,18 +0,0 @@-{- "name": "promise-callback-demo",- "main": "test/index.js",- "private": true,- "dependencies": {- "node-addon-api": "^4.0.0"- },- "scripts": {- "install": "node-gyp rebuild && node test"- },- "engines": {- "node": ">= 10.16.0"- },- "version": "1.0.0",- "description": "An promise callback demo",- "author": "Node-API Team",- "license": "ISC"-}
promise-callback-demo/node-addon-api/src/promise_callback_demo.cc
@@ -1,121 +0,0 @@-#include <napi.h>-#include <future>-#include <iostream>-#include <thread>--using namespace Napi;--class PromiseCallbackDemo;-using Context = PromiseCallbackDemo;-using DataType = std::promise<std::string>;-void CallJs(Napi::Env env, Function callback, Context* context, DataType* data);-using TSFN = TypedThreadSafeFunction<Context, DataType, CallJs>;-using FinalizerDataType = void;--class PromiseCallbackDemo : public Napi::ObjectWrap<PromiseCallbackDemo> {- public:- PromiseCallbackDemo(const Napi::CallbackInfo&);-- static Napi::Function GetClass(Napi::Env);-- std::thread native_thread;- TSFN tsfn;-};--void thread_entry(PromiseCallbackDemo* obj) {- for (auto i = 0; i < 10; ++i) {- std::promise<std::string> promise;- auto future = promise.get_future();- obj->tsfn.NonBlockingCall(&promise);-- // With exceptions disabled, STL calls will abort the thread, since we- // cannot try/catch, as the calls to `std::make_exception_ptr()` will abort.- try {- auto result = future.get();- std::cout << "Result from JS, call " << i << ": " << result << "\n";- } catch (const std::exception& e) {- std::cout << "Error from JS, call " << i << ": " << e.what() << "\n";- }- }- obj->tsfn.Release();-}--void CallJs(Napi::Env env,- Function callback,- Context* context,- DataType* data) {- // Is the JavaScript environment still available to call into, eg. the TSFN is- // not aborted- if (env != nullptr) {- auto jsPromise = callback.Call({}).As<Napi::Object>();- auto then = jsPromise.Get("then").As<Function>();- // Attach the resolve handler.- // TODO proper error handling (add failure handler)- then.Call(jsPromise,- {Napi::Function::New(env,- [=](const CallbackInfo& info) {- auto result =- info[0].As<String>().Utf8Value();- data->set_value(result);- }),- Napi::Function::New(env, [=](const CallbackInfo& info) {- auto result = info[0].As<String>().Utf8Value();- data->set_exception(std::make_exception_ptr(- std::runtime_error("Error in jsCallback")));- })});-- } else {- data->set_exception(std::make_exception_ptr(- std::runtime_error("Environment is shut down")));- }-}--PromiseCallbackDemo::PromiseCallbackDemo(const Napi::CallbackInfo& info)- : ObjectWrap(info) {- Napi::Env env = info.Env();-- // Argument checking- if (info.Length() < 1) {- Napi::TypeError::New(env, "Wrong number of arguments")- .ThrowAsJavaScriptException();- return;- }-- if (!info[0].IsFunction()) {- Napi::TypeError::New(env, "First argument must be a function")- .ThrowAsJavaScriptException();- return;- }-- // Since we are storing the std::thread on this ObjectWrap instance, we should- // reference it so it doesn't get garbage collected, and unreference it on- // TSFN finalize.- Ref();-- tsfn = TSFN::New(- env,- info[0].As<Function>(), // JavaScript function called asynchronously- "PromiseCallbackDemo", // Name- 0, // Unlimited queue- 1, // Only one thread will use this initially- this, // Context- [](Napi::Env,- FinalizerDataType*,- Context* ctx) { // Finalizer used to clean threads up- ctx->Unref();- ctx->native_thread.join();- });-- native_thread = std::thread(thread_entry, this);-}--Napi::Function PromiseCallbackDemo::GetClass(Napi::Env env) {- return DefineClass(env, "PromiseCallbackDemo", {});-}--Napi::Object Init(Napi::Env env, Napi::Object exports) {- exports["PromiseCallbackDemo"] = PromiseCallbackDemo::GetClass(env);- return exports;-}--NODE_API_MODULE(addon, Init)
promise-callback-demo/node-addon-api/test/index.js
@@ -1,11 +0,0 @@-const { PromiseCallbackDemo } = require('../build/Release/promise-callback-demo');--let shouldFail = false;-// Resolve or reject a promise with an ISO date format after 100 milliseconds-function jsCallback() {- return new Promise((resolve, reject) => setTimeout(() => {- ((shouldFail = !shouldFail) ? reject : resolve)(new Date().toISOString());- }, 100));-}--new PromiseCallbackDemo(jsCallback);
src/1-getting-started/1_hello_world/README.md
@@ -0,0 +1,8 @@+## Example 1: *Hello world*++To get started let's make a small addon which is the C++ equivalent of+the following JavaScript code:++```js+module.exports.hello = function() { return 'world'; };+```
src/1-getting-started/1_hello_world/nan/binding.gyp
@@ -0,0 +1,11 @@+{+ "targets": [+ {+ "target_name": "hello",+ "sources": [ "hello.cc" ],+ "include_dirs": [+ "<!(node -e \"require('nan')\")"+ ]+ }+ ]+}
src/1-getting-started/1_hello_world/nan/hello.cc
@@ -0,0 +1,17 @@+#include <nan.h>++void Method(const Nan::FunctionCallbackInfo<v8::Value>& info) {+ info.GetReturnValue().Set(Nan::New("world").ToLocalChecked());+}++void Init(v8::Local<v8::Object> exports) {+ v8::Local<v8::Context> context =+ exports->GetCreationContext().ToLocalChecked();+ exports->Set(context,+ Nan::New("hello").ToLocalChecked(),+ Nan::New<v8::FunctionTemplate>(Method)+ ->GetFunction(context)+ .ToLocalChecked());+}++NODE_MODULE(hello, Init)
src/1-getting-started/1_hello_world/nan/hello.js
@@ -0,0 +1,3 @@+var addon = require('bindings')('hello');++console.log(addon.hello()); // 'world'
src/1-getting-started/1_hello_world/nan/package.json
@@ -0,0 +1,15 @@+{+ "name": "hello_world",+ "version": "0.0.0",+ "description": "Node.js Addons Example #1",+ "main": "hello.js",+ "private": true,+ "dependencies": {+ "bindings": "~1.2.1",+ "nan": "^2.0.0"+ },+ "scripts": {+ "test": "node hello.js"+ },+ "gypfile": true+}
src/1-getting-started/1_hello_world/napi/binding.gyp
@@ -0,0 +1,8 @@+{+ "targets": [+ {+ "target_name": "hello",+ "sources": [ "hello.c" ]+ }+ ]+}
src/1-getting-started/1_hello_world/napi/hello.c
@@ -0,0 +1,23 @@+#include <assert.h>+#include <node_api.h>++static napi_value Method(napi_env env, napi_callback_info info) {+ napi_status status;+ napi_value world;+ status = napi_create_string_utf8(env, "world", 5, &world);+ assert(status == napi_ok);+ return world;+}++#define DECLARE_NAPI_METHOD(name, func) \+ { name, 0, func, 0, 0, 0, napi_default, 0 }++static napi_value Init(napi_env env, napi_value exports) {+ napi_status status;+ napi_property_descriptor desc = DECLARE_NAPI_METHOD("hello", Method);+ status = napi_define_properties(env, exports, 1, &desc);+ assert(status == napi_ok);+ return exports;+}++NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)
src/1-getting-started/1_hello_world/napi/hello.js
@@ -0,0 +1,3 @@+var addon = require('bindings')('hello');++console.log(addon.hello()); // 'world'
src/1-getting-started/1_hello_world/napi/package.json
@@ -0,0 +1,14 @@+{+ "name": "hello_world",+ "version": "0.0.0",+ "description": "Node.js Addons Example #1",+ "main": "hello.js",+ "private": true,+ "dependencies": {+ "bindings": "~1.2.1"+ },+ "scripts": {+ "test": "node hello.js"+ },+ "gypfile": true+}
src/1-getting-started/1_hello_world/node-addon-api-addon-class/binding.gyp
@@ -0,0 +1,14 @@+{+ "targets": [+ {+ "target_name": "hello",+ "cflags!": [ "-fno-exceptions" ],+ "cflags_cc!": [ "-fno-exceptions" ],+ "sources": [ "hello.cc" ],+ "include_dirs": [+ "<!@(node -p \"require('node-addon-api').include\")"+ ],+ 'defines': ['NAPI_DISABLE_CPP_EXCEPTIONS'],+ }+ ]+}
src/1-getting-started/1_hello_world/node-addon-api-addon-class/hello.cc
@@ -0,0 +1,16 @@+#include <napi.h>++class HelloAddon : public Napi::Addon<HelloAddon> {+ public:+ HelloAddon(Napi::Env env, Napi::Object exports) {+ DefineAddon(exports,+ {InstanceMethod("hello", &HelloAddon::Hello, napi_enumerable)});+ }++ private:+ Napi::Value Hello(const Napi::CallbackInfo& info) {+ return Napi::String::New(info.Env(), "world");+ }+};++NODE_API_ADDON(HelloAddon)
src/1-getting-started/1_hello_world/node-addon-api-addon-class/hello.js
@@ -0,0 +1,3 @@+var addon = require('bindings')('hello');++console.log(addon.hello()); // 'world'
src/1-getting-started/1_hello_world/node-addon-api-addon-class/package.json
@@ -0,0 +1,15 @@+{+ "name": "hello_world",+ "version": "0.0.0",+ "description": "Node.js Addons Example #1",+ "main": "hello.js",+ "private": true,+ "dependencies": {+ "bindings": "~1.5.0",+ "node-addon-api": "^7.0.0"+ },+ "scripts": {+ "test": "node hello.js"+ },+ "gypfile": true+}
src/1-getting-started/1_hello_world/node-addon-api/binding.gyp
@@ -0,0 +1,14 @@+{+ "targets": [+ {+ "target_name": "hello",+ "cflags!": [ "-fno-exceptions" ],+ "cflags_cc!": [ "-fno-exceptions" ],+ "sources": [ "hello.cc" ],+ "include_dirs": [+ "<!@(node -p \"require('node-addon-api').include\")"+ ],+ 'defines': [ 'NAPI_DISABLE_CPP_EXCEPTIONS' ],+ }+ ]+}
src/1-getting-started/1_hello_world/node-addon-api/hello.cc
@@ -0,0 +1,14 @@+#include <napi.h>++Napi::String Method(const Napi::CallbackInfo& info) {+ Napi::Env env = info.Env();+ return Napi::String::New(env, "world");+}++Napi::Object Init(Napi::Env env, Napi::Object exports) {+ exports.Set(Napi::String::New(env, "hello"),+ Napi::Function::New(env, Method));+ return exports;+}++NODE_API_MODULE(hello, Init)
src/1-getting-started/1_hello_world/node-addon-api/hello.js
@@ -0,0 +1,3 @@+var addon = require('bindings')('hello');++console.log(addon.hello()); // 'world'
src/1-getting-started/1_hello_world/node-addon-api/package.json
@@ -0,0 +1,15 @@+{+ "name": "hello_world",+ "version": "0.0.0",+ "description": "Node.js Addons Example #1",+ "main": "hello.js",+ "private": true,+ "dependencies": {+ "bindings": "~1.2.1",+ "node-addon-api": "^1.0.0"+ },+ "scripts": {+ "test": "node hello.js"+ },+ "gypfile": true+}
src/1-getting-started/2_function_arguments/README.md
@@ -0,0 +1,2 @@+## Example 2: *Function arguments*+
src/1-getting-started/2_function_arguments/nan/addon.cc
@@ -0,0 +1,33 @@+#include <nan.h>++void Add(const Nan::FunctionCallbackInfo<v8::Value>& info) {+ v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext();++ if (info.Length() < 2) {+ Nan::ThrowTypeError("Wrong number of arguments");+ return;+ }++ if (!info[0]->IsNumber() || !info[1]->IsNumber()) {+ Nan::ThrowTypeError("Wrong arguments");+ return;+ }++ double arg0 = info[0]->NumberValue(context).FromJust();+ double arg1 = info[1]->NumberValue(context).FromJust();+ v8::Local<v8::Number> num = Nan::New(arg0 + arg1);++ info.GetReturnValue().Set(num);+}++void Init(v8::Local<v8::Object> exports) {+ v8::Local<v8::Context> context =+ exports->GetCreationContext().ToLocalChecked();+ exports->Set(context,+ Nan::New("add").ToLocalChecked(),+ Nan::New<v8::FunctionTemplate>(Add)+ ->GetFunction(context)+ .ToLocalChecked());+}++NODE_MODULE(addon, Init)
src/1-getting-started/2_function_arguments/nan/addon.js
@@ -0,0 +1,3 @@+var addon = require('bindings')('addon.node')++console.log('This should be eight:', addon.add(3, 5))
src/1-getting-started/2_function_arguments/nan/binding.gyp
@@ -0,0 +1,11 @@+{+ "targets": [+ {+ "target_name": "addon",+ "sources": [ "addon.cc" ],+ "include_dirs": [+ "<!(node -e \"require('nan')\")"+ ]+ }+ ]+}
src/1-getting-started/2_function_arguments/nan/package.json
@@ -0,0 +1,15 @@+{+ "name": "function_arguments",+ "version": "0.0.0",+ "description": "Node.js Addons Example #2",+ "main": "addon.js",+ "private": true,+ "dependencies": {+ "bindings": "~1.5.0",+ "nan": "^2.0.0"+ },+ "scripts": {+ "test": "node addon.js"+ },+ "gypfile": true+}
src/1-getting-started/2_function_arguments/napi/addon.c
@@ -0,0 +1,56 @@+#include <assert.h>+#include <node_api.h>+#include <stdio.h>+static napi_value Add(napi_env env, napi_callback_info info) {+ napi_status status;++ size_t argc = 2;+ napi_value args[2];+ status = napi_get_cb_info(env, info, &argc, args, NULL, NULL);+ assert(status == napi_ok);++ if (argc < 2) {+ napi_throw_type_error(env, NULL, "Wrong number of arguments");+ return NULL;+ }++ napi_valuetype valuetype0;+ status = napi_typeof(env, args[0], &valuetype0);+ assert(status == napi_ok);++ napi_valuetype valuetype1;+ status = napi_typeof(env, args[1], &valuetype1);+ assert(status == napi_ok);++ if (valuetype0 != napi_number || valuetype1 != napi_number) {+ napi_throw_type_error(env, NULL, "Wrong arguments");+ return NULL;+ }++ double value0;+ status = napi_get_value_double(env, args[0], &value0);+ assert(status == napi_ok);++ double value1;+ status = napi_get_value_double(env, args[1], &value1);+ assert(status == napi_ok);++ napi_value sum;+ status = napi_create_double(env, value0 + value1, &sum);+ assert(status == napi_ok);++ return sum;+}++#define DECLARE_NAPI_METHOD(name, func) \+ { name, 0, func, 0, 0, 0, napi_default, 0 }++napi_value Init(napi_env env, napi_value exports) {+ napi_status status;+ napi_property_descriptor addDescriptor = DECLARE_NAPI_METHOD("add", Add);+ status = napi_define_properties(env, exports, 1, &addDescriptor);+ assert(status == napi_ok);+ return exports;+}++NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)
src/1-getting-started/2_function_arguments/napi/addon.js
@@ -0,0 +1,3 @@+var addon = require('bindings')('addon.node')++console.log('This should be eight:', addon.add(3, 5))
src/1-getting-started/2_function_arguments/napi/binding.gyp
@@ -0,0 +1,8 @@+{+ "targets": [+ {+ "target_name": "addon",+ "sources": [ "addon.c" ]+ }+ ]+}
src/1-getting-started/2_function_arguments/napi/package.json
@@ -0,0 +1,14 @@+{+ "name": "function_arguments",+ "version": "0.0.0",+ "description": "Node.js Addons Example #2",+ "main": "addon.js",+ "private": true,+ "dependencies": {+ "bindings": "~1.2.1"+ },+ "scripts": {+ "test": "node addon.js"+ },+ "gypfile": true+}
src/1-getting-started/2_function_arguments/node-addon-api/addon.cc
@@ -0,0 +1,29 @@+#include <napi.h>++Napi::Value Add(const Napi::CallbackInfo& info) {+ Napi::Env env = info.Env();++ if (info.Length() < 2) {+ Napi::TypeError::New(env, "Wrong number of arguments")+ .ThrowAsJavaScriptException();+ return env.Null();+ }++ if (!info[0].IsNumber() || !info[1].IsNumber()) {+ Napi::TypeError::New(env, "Wrong arguments").ThrowAsJavaScriptException();+ return env.Null();+ }++ double arg0 = info[0].As<Napi::Number>().DoubleValue();+ double arg1 = info[1].As<Napi::Number>().DoubleValue();+ Napi::Number num = Napi::Number::New(env, arg0 + arg1);++ return num;+}++Napi::Object Init(Napi::Env env, Napi::Object exports) {+ exports.Set(Napi::String::New(env, "add"), Napi::Function::New(env, Add));+ return exports;+}++NODE_API_MODULE(addon, Init)
src/1-getting-started/2_function_arguments/node-addon-api/addon.js
@@ -0,0 +1,3 @@+var addon = require('bindings')('addon.node')++console.log('This should be eight:', addon.add(3, 5))
src/1-getting-started/2_function_arguments/node-addon-api/binding.gyp
@@ -0,0 +1,14 @@+{+ "targets": [+ {+ "target_name": "addon",+ "cflags!": [ "-fno-exceptions" ],+ "cflags_cc!": [ "-fno-exceptions" ],+ "sources": [ "addon.cc" ],+ "include_dirs": [+ "<!@(node -p \"require('node-addon-api').include\")"+ ],+ 'defines': [ 'NAPI_DISABLE_CPP_EXCEPTIONS' ],+ }+ ]+}
src/1-getting-started/2_function_arguments/node-addon-api/package.json
@@ -0,0 +1,15 @@+{+ "name": "function_arguments",+ "version": "0.0.0",+ "description": "Node.js Addons Example #2",+ "main": "addon.js",+ "private": true,+ "dependencies": {+ "bindings": "~1.5.0",+ "node-addon-api": "^1.0.0"+ },+ "scripts": {+ "test": "node addon.js"+ },+ "gypfile": true+}
src/1-getting-started/3_callbacks/nan/addon.cc
@@ -0,0 +1,15 @@+#include <nan.h>++void RunCallback(const Nan::FunctionCallbackInfo<v8::Value>& info) {+ v8::Local<v8::Function> cb = info[0].As<v8::Function>();+ const unsigned argc = 1;+ v8::Local<v8::Value> argv[argc] = {Nan::New("hello world").ToLocalChecked()};+ Nan::AsyncResource resource("nan:makeCallback");+ resource.runInAsyncScope(Nan::GetCurrentContext()->Global(), cb, argc, argv);+}++void Init(v8::Local<v8::Object> exports, v8::Local<v8::Object> module) {+ Nan::SetMethod(module, "exports", RunCallback);+}++NODE_MODULE(addon, Init)
src/1-getting-started/3_callbacks/nan/addon.js
@@ -0,0 +1,5 @@+var addon = require('bindings')('addon');++addon(function(msg){+ console.log(msg); // 'hello world'+});
src/1-getting-started/3_callbacks/nan/binding.gyp
@@ -0,0 +1,11 @@+{+ "targets": [+ {+ "target_name": "addon",+ "sources": [ "addon.cc" ],+ "include_dirs": [+ "<!(node -e \"require('nan')\")"+ ]+ }+ ]+}
src/1-getting-started/3_callbacks/nan/package.json
@@ -0,0 +1,12 @@+{+ "name": "callbacks",+ "version": "0.0.0",+ "description": "Node.js Addons Example #3",+ "main": "addon.js",+ "private": true,+ "gypfile": true,+ "dependencies": {+ "bindings": "~1.5.0",+ "nan": "^2.14.0"+ }+}
src/1-getting-started/3_callbacks/napi/addon.c
@@ -0,0 +1,37 @@+#include <assert.h>+#include <node_api.h>++static napi_value RunCallback(napi_env env, const napi_callback_info info) {+ napi_status status;++ size_t argc = 1;+ napi_value args[1];+ status = napi_get_cb_info(env, info, &argc, args, NULL, NULL);+ assert(status == napi_ok);++ napi_value cb = args[0];++ napi_value argv[1];+ status = napi_create_string_utf8(env, "hello world", NAPI_AUTO_LENGTH, argv);+ assert(status == napi_ok);++ napi_value global;+ status = napi_get_global(env, &global);+ assert(status == napi_ok);++ napi_value result;+ status = napi_call_function(env, global, cb, 1, argv, &result);+ assert(status == napi_ok);++ return NULL;+}++static napi_value Init(napi_env env, napi_value exports) {+ napi_value new_exports;+ napi_status status = napi_create_function(+ env, "", NAPI_AUTO_LENGTH, RunCallback, NULL, &new_exports);+ assert(status == napi_ok);+ return new_exports;+}++NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)
src/1-getting-started/3_callbacks/napi/addon.js
@@ -0,0 +1,5 @@+var addon = require('bindings')('addon');++addon(function(msg){+ console.log(msg); // 'hello world'+});
src/1-getting-started/3_callbacks/napi/binding.gyp
@@ -0,0 +1,8 @@+{+ "targets": [+ {+ "target_name": "addon",+ "sources": [ "addon.c" ]+ }+ ]+}
src/1-getting-started/3_callbacks/napi/package.json
@@ -0,0 +1,11 @@+{+ "name": "napi_callbacks",+ "version": "0.0.0",+ "description": "Node.js Addons Example #3",+ "main": "addon.js",+ "private": true,+ "gypfile": true,+ "dependencies": {+ "bindings": "~1.2.1"+ }+}
src/1-getting-started/3_callbacks/node-addon-api/addon.cc
@@ -0,0 +1,13 @@+#include <napi.h>++void RunCallback(const Napi::CallbackInfo& info) {+ Napi::Env env = info.Env();+ Napi::Function cb = info[0].As<Napi::Function>();+ cb.Call(env.Global(), {Napi::String::New(env, "hello world")});+}++Napi::Object Init(Napi::Env env, Napi::Object exports) {+ return Napi::Function::New(env, RunCallback);+}++NODE_API_MODULE(addon, Init)
src/1-getting-started/3_callbacks/node-addon-api/addon.js
@@ -0,0 +1,5 @@+var addon = require('bindings')('addon');++addon(function(msg){+ console.log(msg); // 'hello world'+});
src/1-getting-started/3_callbacks/node-addon-api/binding.gyp
@@ -0,0 +1,14 @@+{+ "targets": [+ {+ "target_name": "addon",+ "cflags!": [ "-fno-exceptions" ],+ "cflags_cc!": [ "-fno-exceptions" ],+ "sources": [ "addon.cc" ],+ "include_dirs": [+ "<!@(node -p \"require('node-addon-api').include\")"+ ],+ 'defines': [ 'NAPI_DISABLE_CPP_EXCEPTIONS' ],+ }+ ]+}
src/1-getting-started/3_callbacks/node-addon-api/package.json
@@ -0,0 +1,12 @@+{+ "name": "callbacks",+ "version": "0.0.0",+ "description": "Node.js Addons Example #3",+ "main": "addon.js",+ "private": true,+ "gypfile": true,+ "dependencies": {+ "bindings": "~1.5.0",+ "node-addon-api": "^1.0.0"+ }+}
src/1-getting-started/4_object_factory/nan/addon.cc
@@ -0,0 +1,23 @@+#include <nan.h>++void CreateObject(const Nan::FunctionCallbackInfo<v8::Value>& info) {+ v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext();+ v8::Local<v8::Object> obj = Nan::New<v8::Object>();+ obj->Set(context,+ Nan::New("msg").ToLocalChecked(),+ info[0]->ToString(context).ToLocalChecked());++ info.GetReturnValue().Set(obj);+}++void Init(v8::Local<v8::Object> exports, v8::Local<v8::Object> module) {+ v8::Local<v8::Context> context =+ exports->GetCreationContext().ToLocalChecked();+ module->Set(context,+ Nan::New("exports").ToLocalChecked(),+ Nan::New<v8::FunctionTemplate>(CreateObject)+ ->GetFunction(context)+ .ToLocalChecked());+}++NODE_MODULE(addon, Init)
src/1-getting-started/4_object_factory/nan/addon.js
@@ -0,0 +1,5 @@+var addon = require('bindings')('addon');++var obj1 = addon('hello');+var obj2 = addon('world');+console.log(obj1.msg+' '+obj2.msg); // 'hello world'
src/1-getting-started/4_object_factory/nan/binding.gyp
@@ -0,0 +1,11 @@+{+ "targets": [+ {+ "target_name": "addon",+ "sources": [ "addon.cc" ],+ "include_dirs": [+ "<!(node -e \"require('nan')\")"+ ]+ }+ ]+}
src/1-getting-started/4_object_factory/nan/package.json
@@ -0,0 +1,12 @@+{+ "name": "object_factory",+ "version": "0.0.0",+ "description": "Node.js Addons Example #4",+ "main": "addon.js",+ "private": true,+ "gypfile": true,+ "dependencies": {+ "bindings": "~1.5.0",+ "nan": "^2.14.0"+ }+}
src/1-getting-started/4_object_factory/napi/addon.c
@@ -0,0 +1,30 @@+#include <assert.h>+#include <node_api.h>++static napi_value CreateObject(napi_env env, const napi_callback_info info) {+ napi_status status;++ size_t argc = 1;+ napi_value args[1];+ status = napi_get_cb_info(env, info, &argc, args, NULL, NULL);+ assert(status == napi_ok);++ napi_value obj;+ status = napi_create_object(env, &obj);+ assert(status == napi_ok);++ status = napi_set_named_property(env, obj, "msg", args[0]);+ assert(status == napi_ok);++ return obj;+}++static napi_value Init(napi_env env, napi_value exports) {+ napi_value new_exports;+ napi_status status = napi_create_function(+ env, "", NAPI_AUTO_LENGTH, CreateObject, NULL, &new_exports);+ assert(status == napi_ok);+ return new_exports;+}++NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)
src/1-getting-started/4_object_factory/napi/addon.js
@@ -0,0 +1,5 @@+var addon = require('bindings')('addon');++var obj1 = addon('hello');+var obj2 = addon('world');+console.log(obj1.msg+' '+obj2.msg); // 'hello world'
src/1-getting-started/4_object_factory/napi/binding.gyp
@@ -0,0 +1,8 @@+{+ "targets": [+ {+ "target_name": "addon",+ "sources": [ "addon.c" ]+ }+ ]+}
src/1-getting-started/4_object_factory/napi/package.json
@@ -0,0 +1,11 @@+{+ "name": "object_factory",+ "version": "0.0.0",+ "description": "Node.js Addons Example #4",+ "main": "addon.js",+ "private": true,+ "gypfile": true,+ "dependencies": {+ "bindings": "~1.2.1"+ }+}
src/1-getting-started/4_object_factory/node-addon-api/addon.cc
@@ -0,0 +1,15 @@+#include <napi.h>++Napi::Object CreateObject(const Napi::CallbackInfo& info) {+ Napi::Env env = info.Env();+ Napi::Object obj = Napi::Object::New(env);+ obj.Set(Napi::String::New(env, "msg"), info[0].ToString());++ return obj;+}++Napi::Object Init(Napi::Env env, Napi::Object exports) {+ return Napi::Function::New(env, CreateObject, "createObject");+}++NODE_API_MODULE(addon, Init)
src/1-getting-started/4_object_factory/node-addon-api/addon.js
@@ -0,0 +1,5 @@+var addon = require('bindings')('addon');++var obj1 = addon('hello');+var obj2 = addon('world');+console.log(obj1.msg+' '+obj2.msg); // 'hello world'
src/1-getting-started/4_object_factory/node-addon-api/binding.gyp
@@ -0,0 +1,14 @@+{+ "targets": [+ {+ "target_name": "addon",+ "cflags!": [ "-fno-exceptions" ],+ "cflags_cc!": [ "-fno-exceptions" ],+ "sources": [ "addon.cc" ],+ "include_dirs": [+ "<!@(node -p \"require('node-addon-api').include\")"+ ],+ 'defines': [ 'NAPI_DISABLE_CPP_EXCEPTIONS' ],+ }+ ]+}
src/1-getting-started/4_object_factory/node-addon-api/package.json
@@ -0,0 +1,12 @@+{+ "name": "object_factory",+ "version": "0.0.0",+ "description": "Node.js Addons Example #4",+ "main": "addon.js",+ "private": true,+ "gypfile": true,+ "dependencies": {+ "bindings": "~1.2.1",+ "node-addon-api": "^1.0.0"+ }+}
src/1-getting-started/5_function_factory/nan/addon.cc
@@ -0,0 +1,23 @@+#include <nan.h>++void MyFunction(const Nan::FunctionCallbackInfo<v8::Value>& info) {+ info.GetReturnValue().Set(Nan::New("hello world").ToLocalChecked());+}++void CreateFunction(const Nan::FunctionCallbackInfo<v8::Value>& info) {+ v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext();+ v8::Local<v8::FunctionTemplate> tpl =+ Nan::New<v8::FunctionTemplate>(MyFunction);+ v8::Local<v8::Function> fn = tpl->GetFunction(context).ToLocalChecked();++ // omit this to make it anonymous+ fn->SetName(Nan::New("theFunction").ToLocalChecked());++ info.GetReturnValue().Set(fn);+}++void Init(v8::Local<v8::Object> exports, v8::Local<v8::Object> module) {+ Nan::SetMethod(module, "exports", CreateFunction);+}++NODE_MODULE(addon, Init)
src/1-getting-started/5_function_factory/nan/addon.js
@@ -0,0 +1,4 @@+var addon = require('bindings')('addon');++var fn = addon();+console.log(fn()); // 'hello world'
src/1-getting-started/5_function_factory/nan/binding.gyp
@@ -0,0 +1,11 @@+{+ "targets": [+ {+ "target_name": "addon",+ "sources": [ "addon.cc" ],+ "include_dirs": [+ "<!(node -e \"require('nan')\")"+ ]+ }+ ]+}
src/1-getting-started/5_function_factory/nan/package.json
@@ -0,0 +1,12 @@+{+ "name": "function_factory",+ "version": "0.0.0",+ "description": "Node.js Addons Example #5",+ "main": "addon.js",+ "private": true,+ "gypfile": true,+ "dependencies": {+ "bindings": "~1.5.0",+ "nan": "^2.0.0"+ }+}
src/1-getting-started/5_function_factory/napi/addon.c
@@ -0,0 +1,33 @@+#include <node_api.h>+#include <assert.h>++static napi_value MyFunction(napi_env env, napi_callback_info info) {+ napi_status status;++ napi_value str;+ status = napi_create_string_utf8(env, "hello world", NAPI_AUTO_LENGTH, &str);+ assert(status == napi_ok);++ return str;+}++static napi_value CreateFunction(napi_env env, napi_callback_info info) {+ napi_status status;++ napi_value fn;+ status = napi_create_function(+ env, "theFunction", NAPI_AUTO_LENGTH, MyFunction, NULL, &fn);+ assert(status == napi_ok);++ return fn;+}++static napi_value Init(napi_env env, napi_value exports) {+ napi_value new_exports;+ napi_status status = napi_create_function(+ env, "", NAPI_AUTO_LENGTH, CreateFunction, NULL, &new_exports);+ assert(status == napi_ok);+ return new_exports;+}++NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)
src/1-getting-started/5_function_factory/napi/addon.js
@@ -0,0 +1,4 @@+var addon = require('bindings')('addon');++var fn = addon();+console.log(fn()); // 'hello world'
src/1-getting-started/5_function_factory/napi/binding.gyp
@@ -0,0 +1,8 @@+{+ "targets": [+ {+ "target_name": "addon",+ "sources": [ "addon.c" ]+ }+ ]+}
src/1-getting-started/5_function_factory/napi/package.json
@@ -0,0 +1,11 @@+{+ "name": "function_factory",+ "version": "0.0.0",+ "description": "Node.js Addons Example #5",+ "main": "addon.js",+ "private": true,+ "gypfile": true,+ "dependencies": {+ "bindings": "~1.5.0"+ }+}
src/1-getting-started/5_function_factory/node-addon-api/addon.cc
@@ -0,0 +1,18 @@+#include <napi.h>++Napi::String MyFunction(const Napi::CallbackInfo& info) {+ Napi::Env env = info.Env();+ return Napi::String::New(env, "hello world");+}++Napi::Function CreateFunction(const Napi::CallbackInfo& info) {+ Napi::Env env = info.Env();+ Napi::Function fn = Napi::Function::New(env, MyFunction, "theFunction");+ return fn;+}++Napi::Object Init(Napi::Env env, Napi::Object exports) {+ return Napi::Function::New(env, CreateFunction, "createObject");+}++NODE_API_MODULE(addon, Init)
src/1-getting-started/5_function_factory/node-addon-api/addon.js
@@ -0,0 +1,4 @@+var addon = require('bindings')('addon');++var fn = addon();+console.log(fn()); // 'hello world'
src/1-getting-started/5_function_factory/node-addon-api/binding.gyp
@@ -0,0 +1,14 @@+{+ "targets": [+ {+ "target_name": "addon",+ "cflags!": [ "-fno-exceptions" ],+ "cflags_cc!": [ "-fno-exceptions" ],+ "sources": [ "addon.cc" ],+ "include_dirs": [+ "<!@(node -p \"require('node-addon-api').include\")"+ ],+ 'defines': [ 'NAPI_DISABLE_CPP_EXCEPTIONS' ],+ }+ ]+}
src/1-getting-started/5_function_factory/node-addon-api/package.json
@@ -0,0 +1,12 @@+{+ "name": "function_factory",+ "version": "0.0.0",+ "description": "Node.js Addons Example #5",+ "main": "addon.js",+ "private": true,+ "gypfile": true,+ "dependencies": {+ "bindings": "~1.2.1",+ "node-addon-api": "^7.0.0"+ }+}
src/1-getting-started/6_object_wrap/nan/addon.cc
@@ -0,0 +1,8 @@+#include <nan.h>+#include "myobject.h"++void InitAll(v8::Local<v8::Object> exports) {+ MyObject::Init(exports);+}++NODE_MODULE(addon, InitAll)
src/1-getting-started/6_object_wrap/nan/addon.js
@@ -0,0 +1,13 @@+var addon = require('bindings')('addon');++var obj = new addon.MyObject(10);+console.log( obj.plusOne() ); // 11+console.log( obj.plusOne() ); // 12+console.log( obj.plusOne() ); // 13++console.log( obj.multiply().value() ); // 13+console.log( obj.multiply(10).value() ); // 130++var newobj = obj.multiply(-1);+console.log( newobj.value() ); // -13+console.log( obj === newobj ); // false
src/1-getting-started/6_object_wrap/nan/binding.gyp
@@ -0,0 +1,11 @@+{+ "targets": [+ {+ "target_name": "addon",+ "sources": [ "addon.cc", "myobject.cc" ],+ "include_dirs": [+ "<!(node -e \"require('nan')\")"+ ]+ }+ ]+}
src/1-getting-started/6_object_wrap/nan/myobject.cc
@@ -0,0 +1,73 @@+#include "myobject.h"++Nan::Persistent<v8::Function> MyObject::constructor;++MyObject::MyObject(double value) : value_(value) {}++MyObject::~MyObject() {}++void MyObject::Init(v8::Local<v8::Object> exports) {+ v8::Local<v8::Context> context =+ exports->GetCreationContext().ToLocalChecked();+ Nan::HandleScope scope;++ // Prepare constructor template+ v8::Local<v8::FunctionTemplate> tpl = Nan::New<v8::FunctionTemplate>(New);+ tpl->SetClassName(Nan::New("MyObject").ToLocalChecked());+ tpl->InstanceTemplate()->SetInternalFieldCount(1);++ // Prototype+ Nan::SetPrototypeMethod(tpl, "value", GetValue);+ Nan::SetPrototypeMethod(tpl, "plusOne", PlusOne);+ Nan::SetPrototypeMethod(tpl, "multiply", Multiply);++ constructor.Reset(tpl->GetFunction(context).ToLocalChecked());+ exports->Set(context,+ Nan::New("MyObject").ToLocalChecked(),+ tpl->GetFunction(context).ToLocalChecked());+}++void MyObject::New(const Nan::FunctionCallbackInfo<v8::Value>& info) {+ v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext();+ if (info.IsConstructCall()) {+ // Invoked as constructor: `new MyObject(...)`+ double value =+ info[0]->IsUndefined() ? 0 : info[0]->NumberValue(context).FromJust();+ MyObject* obj = new MyObject(value);+ obj->Wrap(info.This());+ info.GetReturnValue().Set(info.This());+ } else {+ // Invoked as plain function `MyObject(...)`, turn into construct call.+ const int argc = 1;+ v8::Local<v8::Value> argv[argc] = {info[0]};+ v8::Local<v8::Function> cons = Nan::New<v8::Function>(constructor);+ info.GetReturnValue().Set(+ cons->NewInstance(context, argc, argv).ToLocalChecked());+ }+}++void MyObject::GetValue(const Nan::FunctionCallbackInfo<v8::Value>& info) {+ MyObject* obj = ObjectWrap::Unwrap<MyObject>(info.Holder());+ info.GetReturnValue().Set(Nan::New(obj->value_));+}++void MyObject::PlusOne(const Nan::FunctionCallbackInfo<v8::Value>& info) {+ MyObject* obj = ObjectWrap::Unwrap<MyObject>(info.Holder());+ obj->value_ += 1;+ info.GetReturnValue().Set(Nan::New(obj->value_));+}++void MyObject::Multiply(const Nan::FunctionCallbackInfo<v8::Value>& info) {+ v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext();+ MyObject* obj = ObjectWrap::Unwrap<MyObject>(info.Holder());+ double multiple =+ info[0]->IsUndefined() ? 1 : info[0]->NumberValue(context).FromJust();++ v8::Local<v8::Function> cons = Nan::New<v8::Function>(constructor);++ const int argc = 1;+ v8::Local<v8::Value> argv[argc] = {Nan::New(obj->value_ * multiple)};++ info.GetReturnValue().Set(+ cons->NewInstance(context, argc, argv).ToLocalChecked());+}
src/1-getting-started/6_object_wrap/nan/myobject.h
@@ -0,0 +1,22 @@+#ifndef MYOBJECT_H+#define MYOBJECT_H++#include <nan.h>++class MyObject : public Nan::ObjectWrap {+ public:+ static void Init(v8::Local<v8::Object> exports);++ private:+ explicit MyObject(double value = 0);+ ~MyObject();++ static void New(const Nan::FunctionCallbackInfo<v8::Value>& info);+ static void GetValue(const Nan::FunctionCallbackInfo<v8::Value>& info);+ static void PlusOne(const Nan::FunctionCallbackInfo<v8::Value>& info);+ static void Multiply(const Nan::FunctionCallbackInfo<v8::Value>& info);+ static Nan::Persistent<v8::Function> constructor;+ double value_;+};++#endif
src/1-getting-started/6_object_wrap/nan/package.json
@@ -0,0 +1,12 @@+{+ "name": "object_wrap",+ "version": "0.0.0",+ "description": "Node.js Addons Example #6",+ "main": "addon.js",+ "private": true,+ "gypfile": true,+ "dependencies": {+ "bindings": "~1.5.0",+ "nan": "^2.14.0"+ }+}
src/1-getting-started/6_object_wrap/napi/addon.cc
@@ -0,0 +1,7 @@+#include "myobject.h"++napi_value Init(napi_env env, napi_value exports) {+ return MyObject::Init(env, exports);+}++NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)
src/1-getting-started/6_object_wrap/napi/addon.js
@@ -0,0 +1,13 @@+var addon = require('bindings')('addon');++var obj = new addon.MyObject(10);+console.log( obj.plusOne() ); // 11+console.log( obj.plusOne() ); // 12+console.log( obj.plusOne() ); // 13++console.log( obj.multiply().value ); // 13+console.log( obj.multiply(10).value ); // 130++var newobj = obj.multiply(-1);+console.log( newobj.value ); // -13+console.log( obj === newobj ); // false
src/1-getting-started/6_object_wrap/napi/binding.gyp
@@ -0,0 +1,8 @@+{+ "targets": [+ {+ "target_name": "addon",+ "sources": [ "addon.cc", "myobject.cc" ]+ }+ ]+}
src/1-getting-started/6_object_wrap/napi/myobject.cc
@@ -0,0 +1,223 @@+#include "myobject.h"+#include <assert.h>++MyObject::MyObject(double value)+ : value_(value), env_(nullptr), wrapper_(nullptr) {}++MyObject::~MyObject() {+ napi_delete_reference(env_, wrapper_);+}++void MyObject::Destructor(napi_env env,+ void* nativeObject,+ void* /*finalize_hint*/) {+ reinterpret_cast<MyObject*>(nativeObject)->~MyObject();+}++#define DECLARE_NAPI_METHOD(name, func) \+ { name, 0, func, 0, 0, 0, napi_default, 0 }++napi_value MyObject::Init(napi_env env, napi_value exports) {+ napi_status status;+ napi_property_descriptor properties[] = {+ {"value", 0, 0, GetValue, SetValue, 0, napi_default, 0},+ DECLARE_NAPI_METHOD("plusOne", PlusOne),+ DECLARE_NAPI_METHOD("multiply", Multiply),+ };++ napi_value cons;+ status = napi_define_class(+ env, "MyObject", NAPI_AUTO_LENGTH, New, nullptr, 3, properties, &cons);+ assert(status == napi_ok);++ // We will need the constructor `cons` later during the life cycle of the+ // addon, so we store a persistent reference to it as the instance data for+ // our addon. This will enable us to use `napi_get_instance_data` at any+ // point during the life cycle of our addon to retrieve it. We cannot simply+ // store it as a global static variable, because that will render our addon+ // unable to support Node.js worker threads and multiple contexts on a single+ // thread.+ //+ // The finalizer we pass as a lambda will be called when our addon is unloaded+ // and is responsible for releasing the persistent reference and freeing the+ // heap memory where we stored the persistent reference.+ napi_ref* constructor = new napi_ref;+ status = napi_create_reference(env, cons, 1, constructor);+ assert(status == napi_ok);+ status = napi_set_instance_data(+ env,+ constructor,+ [](napi_env env, void* data, void* hint) {+ napi_ref* constructor = static_cast<napi_ref*>(data);+ napi_status status = napi_delete_reference(env, *constructor);+ assert(status == napi_ok);+ delete constructor;+ },+ nullptr);+ assert(status == napi_ok);++ status = napi_set_named_property(env, exports, "MyObject", cons);+ assert(status == napi_ok);+ return exports;+}++napi_value MyObject::Constructor(napi_env env) {+ void* instance_data = nullptr;+ napi_status status = napi_get_instance_data(env, &instance_data);+ assert(status == napi_ok);+ napi_ref* constructor = static_cast<napi_ref*>(instance_data);++ napi_value cons;+ status = napi_get_reference_value(env, *constructor, &cons);+ assert(status == napi_ok);+ return cons;+}++napi_value MyObject::New(napi_env env, napi_callback_info info) {+ napi_status status;++ napi_value target;+ status = napi_get_new_target(env, info, &target);+ assert(status == napi_ok);+ bool is_constructor = target != nullptr;++ if (is_constructor) {+ // Invoked as constructor: `new MyObject(...)`+ size_t argc = 1;+ napi_value args[1];+ napi_value jsthis;+ status = napi_get_cb_info(env, info, &argc, args, &jsthis, nullptr);+ assert(status == napi_ok);++ double value = 0;++ napi_valuetype valuetype;+ status = napi_typeof(env, args[0], &valuetype);+ assert(status == napi_ok);++ if (valuetype != napi_undefined) {+ status = napi_get_value_double(env, args[0], &value);+ assert(status == napi_ok);+ }++ MyObject* obj = new MyObject(value);++ obj->env_ = env;+ status = napi_wrap(env,+ jsthis,+ reinterpret_cast<void*>(obj),+ MyObject::Destructor,+ nullptr, // finalize_hint+ &obj->wrapper_);+ assert(status == napi_ok);++ return jsthis;+ } else {+ // Invoked as plain function `MyObject(...)`, turn into construct call.+ size_t argc_ = 1;+ napi_value args[1];+ status = napi_get_cb_info(env, info, &argc_, args, nullptr, nullptr);+ assert(status == napi_ok);++ const size_t argc = 1;+ napi_value argv[argc] = {args[0]};++ napi_value instance;+ status = napi_new_instance(env, Constructor(env), argc, argv, &instance);+ assert(status == napi_ok);++ return instance;+ }+}++napi_value MyObject::GetValue(napi_env env, napi_callback_info info) {+ napi_status status;++ napi_value jsthis;+ status = napi_get_cb_info(env, info, nullptr, nullptr, &jsthis, nullptr);+ assert(status == napi_ok);++ MyObject* obj;+ status = napi_unwrap(env, jsthis, reinterpret_cast<void**>(&obj));+ assert(status == napi_ok);++ napi_value num;+ status = napi_create_double(env, obj->value_, &num);+ assert(status == napi_ok);++ return num;+}++napi_value MyObject::SetValue(napi_env env, napi_callback_info info) {+ napi_status status;++ size_t argc = 1;+ napi_value value;+ napi_value jsthis;+ status = napi_get_cb_info(env, info, &argc, &value, &jsthis, nullptr);+ assert(status == napi_ok);++ MyObject* obj;+ status = napi_unwrap(env, jsthis, reinterpret_cast<void**>(&obj));+ assert(status == napi_ok);++ status = napi_get_value_double(env, value, &obj->value_);+ assert(status == napi_ok);++ return nullptr;+}++napi_value MyObject::PlusOne(napi_env env, napi_callback_info info) {+ napi_status status;++ napi_value jsthis;+ status = napi_get_cb_info(env, info, nullptr, nullptr, &jsthis, nullptr);+ assert(status == napi_ok);++ MyObject* obj;+ status = napi_unwrap(env, jsthis, reinterpret_cast<void**>(&obj));+ assert(status == napi_ok);++ obj->value_ += 1;++ napi_value num;+ status = napi_create_double(env, obj->value_, &num);+ assert(status == napi_ok);++ return num;+}++napi_value MyObject::Multiply(napi_env env, napi_callback_info info) {+ napi_status status;++ size_t argc = 1;+ napi_value args[1];+ napi_value jsthis;+ status = napi_get_cb_info(env, info, &argc, args, &jsthis, nullptr);+ assert(status == napi_ok);++ napi_valuetype valuetype;+ status = napi_typeof(env, args[0], &valuetype);+ assert(status == napi_ok);++ double multiple = 1;+ if (valuetype != napi_undefined) {+ status = napi_get_value_double(env, args[0], &multiple);+ assert(status == napi_ok);+ }++ MyObject* obj;+ status = napi_unwrap(env, jsthis, reinterpret_cast<void**>(&obj));+ assert(status == napi_ok);++ const int kArgCount = 1;+ napi_value argv[kArgCount];+ status = napi_create_double(env, obj->value_ * multiple, argv);+ assert(status == napi_ok);++ napi_value instance;+ status = napi_new_instance(env, Constructor(env), kArgCount, argv, &instance);+ assert(status == napi_ok);++ return instance;+}
src/1-getting-started/6_object_wrap/napi/myobject.h
@@ -0,0 +1,27 @@+#ifndef TEST_ADDONS_NAPI_6_OBJECT_WRAP_MYOBJECT_H_+#define TEST_ADDONS_NAPI_6_OBJECT_WRAP_MYOBJECT_H_++#include <node_api.h>++class MyObject {+ public:+ static napi_value Init(napi_env env, napi_value exports);+ static void Destructor(napi_env env, void* nativeObject, void* finalize_hint);++ private:+ explicit MyObject(double value_ = 0);+ ~MyObject();++ static napi_value New(napi_env env, napi_callback_info info);+ static napi_value GetValue(napi_env env, napi_callback_info info);+ static napi_value SetValue(napi_env env, napi_callback_info info);+ static napi_value PlusOne(napi_env env, napi_callback_info info);+ static napi_value Multiply(napi_env env, napi_callback_info info);+ static inline napi_value Constructor(napi_env env);++ double value_;+ napi_env env_;+ napi_ref wrapper_;+};++#endif // TEST_ADDONS_NAPI_6_OBJECT_WRAP_MYOBJECT_H_
src/1-getting-started/6_object_wrap/napi/package.json
@@ -0,0 +1,11 @@+{+ "name": "object_wrap",+ "version": "0.0.0",+ "description": "Node.js Addons Example #6",+ "main": "addon.js",+ "private": true,+ "gypfile": true,+ "dependencies": {+ "bindings": "~1.5.0"+ }+}
src/1-getting-started/6_object_wrap/node-addon-api/addon.cc
@@ -0,0 +1,8 @@+#include <napi.h>+#include "myobject.h"++Napi::Object InitAll(Napi::Env env, Napi::Object exports) {+ return MyObject::Init(env, exports);+}++NODE_API_MODULE(addon, InitAll)
src/1-getting-started/6_object_wrap/node-addon-api/addon.js
@@ -0,0 +1,13 @@+var addon = require('bindings')('addon');++var obj = new addon.MyObject(10);+console.log( obj.plusOne() ); // 11+console.log( obj.plusOne() ); // 12+console.log( obj.plusOne() ); // 13++console.log( obj.multiply().value() ); // 13+console.log( obj.multiply(10).value() ); // 130++var newobj = obj.multiply(-1);+console.log( newobj.value() ); // -13+console.log( obj === newobj ); // false
src/1-getting-started/6_object_wrap/node-addon-api/binding.gyp
@@ -0,0 +1,14 @@+{+ "targets": [+ {+ "target_name": "addon",+ "cflags!": [ "-fno-exceptions" ],+ "cflags_cc!": [ "-fno-exceptions" ],+ "sources": [ "addon.cc", "myobject.cc" ],+ "include_dirs": [+ "<!@(node -p \"require('node-addon-api').include\")"+ ],+ 'defines': [ 'NAPI_DISABLE_CPP_EXCEPTIONS' ],+ }+ ]+}
src/1-getting-started/6_object_wrap/node-addon-api/myobject.cc
@@ -0,0 +1,58 @@+#include "myobject.h"++Napi::Object MyObject::Init(Napi::Env env, Napi::Object exports) {+ Napi::Function func =+ DefineClass(env,+ "MyObject",+ {InstanceMethod("plusOne", &MyObject::PlusOne),+ InstanceMethod("value", &MyObject::GetValue),+ InstanceMethod("multiply", &MyObject::Multiply)});++ Napi::FunctionReference* constructor = new Napi::FunctionReference();+ *constructor = Napi::Persistent(func);+ env.SetInstanceData(constructor);++ exports.Set("MyObject", func);+ return exports;+}++MyObject::MyObject(const Napi::CallbackInfo& info)+ : Napi::ObjectWrap<MyObject>(info) {+ Napi::Env env = info.Env();++ int length = info.Length();++ if (length <= 0 || !info[0].IsNumber()) {+ Napi::TypeError::New(env, "Number expected").ThrowAsJavaScriptException();+ return;+ }++ Napi::Number value = info[0].As<Napi::Number>();+ this->value_ = value.DoubleValue();+}++Napi::Value MyObject::GetValue(const Napi::CallbackInfo& info) {+ double num = this->value_;++ return Napi::Number::New(info.Env(), num);+}++Napi::Value MyObject::PlusOne(const Napi::CallbackInfo& info) {+ this->value_ = this->value_ + 1;++ return MyObject::GetValue(info);+}++Napi::Value MyObject::Multiply(const Napi::CallbackInfo& info) {+ Napi::Number multiple;+ if (info.Length() <= 0 || !info[0].IsNumber()) {+ multiple = Napi::Number::New(info.Env(), 1);+ } else {+ multiple = info[0].As<Napi::Number>();+ }++ Napi::Object obj = info.Env().GetInstanceData<Napi::FunctionReference>()->New(+ {Napi::Number::New(info.Env(), this->value_ * multiple.DoubleValue())});++ return obj;+}
src/1-getting-started/6_object_wrap/node-addon-api/myobject.h
@@ -0,0 +1,19 @@+#ifndef MYOBJECT_H+#define MYOBJECT_H++#include <napi.h>++class MyObject : public Napi::ObjectWrap<MyObject> {+ public:+ static Napi::Object Init(Napi::Env env, Napi::Object exports);+ MyObject(const Napi::CallbackInfo& info);++ private:+ Napi::Value GetValue(const Napi::CallbackInfo& info);+ Napi::Value PlusOne(const Napi::CallbackInfo& info);+ Napi::Value Multiply(const Napi::CallbackInfo& info);++ double value_;+};++#endif
src/1-getting-started/6_object_wrap/node-addon-api/package.json
@@ -0,0 +1,15 @@+{+ "name": "object_wrap",+ "version": "0.0.0",+ "description": "Node.js Addons Example #6",+ "main": "addon.js",+ "private": true,+ "gypfile": true,+ "engines": {+ "node": "~10 >=10.20 || >=12.17"+ },+ "dependencies": {+ "bindings": "~1.5.0",+ "node-addon-api": "^3.0.0"+ }+}
src/1-getting-started/7_factory_wrap/nan/addon.cc
@@ -0,0 +1,23 @@+#include <nan.h>+#include "myobject.h"++void CreateObject(const Nan::FunctionCallbackInfo<v8::Value>& info) {+ info.GetReturnValue().Set(MyObject::NewInstance(info[0]));+}++void InitAll(v8::Local<v8::Object> exports, v8::Local<v8::Object> module) {+ v8::Local<v8::Context> context =+ exports->GetCreationContext().ToLocalChecked();++ Nan::HandleScope scope;++ MyObject::Init();++ module->Set(context,+ Nan::New("exports").ToLocalChecked(),+ Nan::New<v8::FunctionTemplate>(CreateObject)+ ->GetFunction(context)+ .ToLocalChecked());+}++NODE_MODULE(addon, InitAll)
src/1-getting-started/7_factory_wrap/nan/addon.js
@@ -0,0 +1,11 @@+var createObject = require('bindings')('addon');++var obj = createObject(10);+console.log( obj.plusOne() ); // 11+console.log( obj.plusOne() ); // 12+console.log( obj.plusOne() ); // 13++var obj2 = createObject(20);+console.log( obj2.plusOne() ); // 21+console.log( obj2.plusOne() ); // 22+console.log( obj2.plusOne() ); // 23
src/1-getting-started/7_factory_wrap/nan/binding.gyp
@@ -0,0 +1,11 @@+{+ "targets": [+ {+ "target_name": "addon",+ "sources": [ "addon.cc", "myobject.cc" ],+ "include_dirs": [+ "<!(node -e \"require('nan')\")"+ ]+ }+ ]+}
src/1-getting-started/7_factory_wrap/nan/myobject.cc
@@ -0,0 +1,56 @@+#include "myobject.h"+#include <nan.h>++using namespace v8;++MyObject::MyObject(){};+MyObject::~MyObject(){};++Nan::Persistent<v8::Function> MyObject::constructor;++void MyObject::Init() {+ Nan::HandleScope scope;++ // Prepare constructor template+ v8::Local<v8::FunctionTemplate> tpl = Nan::New<v8::FunctionTemplate>(New);+ tpl->SetClassName(Nan::New("MyObject").ToLocalChecked());+ tpl->InstanceTemplate()->SetInternalFieldCount(1);+ // Prototype+ tpl->PrototypeTemplate()->Set(Nan::New("plusOne").ToLocalChecked(),+ Nan::New<v8::FunctionTemplate>(PlusOne));++ constructor.Reset(+ tpl->GetFunction(Nan::GetCurrentContext()).ToLocalChecked());+}++void MyObject::New(const Nan::FunctionCallbackInfo<v8::Value>& info) {+ v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext();++ MyObject* obj = new MyObject();+ obj->counter_ =+ info[0]->IsUndefined() ? 0 : info[0]->NumberValue(context).FromJust();+ obj->Wrap(info.This());++ info.GetReturnValue().Set(info.This());+}++v8::Local<v8::Object> MyObject::NewInstance(v8::Local<v8::Value> arg) {+ Nan::EscapableHandleScope scope;++ const unsigned argc = 1;+ v8::Local<v8::Value> argv[argc] = {arg};+ v8::Local<v8::Function> cons = Nan::New<v8::Function>(constructor);+ v8::Local<v8::Context> context =+ v8::Isolate::GetCurrent()->GetCurrentContext();+ v8::Local<v8::Object> instance =+ cons->NewInstance(context, argc, argv).ToLocalChecked();++ return scope.Escape(instance);+}++void MyObject::PlusOne(const Nan::FunctionCallbackInfo<v8::Value>& info) {+ MyObject* obj = ObjectWrap::Unwrap<MyObject>(info.This());+ obj->counter_ += 1;++ info.GetReturnValue().Set(Nan::New(obj->counter_));+}
src/1-getting-started/7_factory_wrap/nan/myobject.h
@@ -0,0 +1,21 @@+#ifndef MYOBJECT_H+#define MYOBJECT_H++#include <nan.h>++class MyObject : public Nan::ObjectWrap {+ public:+ static void Init();+ static v8::Local<v8::Object> NewInstance(v8::Local<v8::Value> arg);++ private:+ MyObject();+ ~MyObject();++ static Nan::Persistent<v8::Function> constructor;+ static void New(const Nan::FunctionCallbackInfo<v8::Value>& info);+ static void PlusOne(const Nan::FunctionCallbackInfo<v8::Value>& info);+ double counter_;+};++#endif
src/1-getting-started/7_factory_wrap/nan/package.json
@@ -0,0 +1,12 @@+{+ "name": "factory_wrap",+ "version": "0.0.0",+ "description": "Node.js Addons Example #7",+ "main": "addon.js",+ "private": true,+ "gypfile": true,+ "dependencies": {+ "bindings": "~1.2.1",+ "nan": "^2.0.0"+ }+}
src/1-getting-started/7_factory_wrap/napi/addon.cc
@@ -0,0 +1,30 @@+#include <assert.h>+#include "myobject.h"++napi_value CreateObject(napi_env env, napi_callback_info info) {+ napi_status status;++ size_t argc = 1;+ napi_value args[1];+ status = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);+ assert(status == napi_ok);++ napi_value instance;+ status = MyObject::NewInstance(env, args[0], &instance);+ assert(status == napi_ok);++ return instance;+}++napi_value Init(napi_env env, napi_value exports) {+ napi_status status = MyObject::Init(env);+ assert(status == napi_ok);++ napi_value new_exports;+ status = napi_create_function(+ env, "", NAPI_AUTO_LENGTH, CreateObject, nullptr, &new_exports);+ assert(status == napi_ok);+ return new_exports;+}++NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)
src/1-getting-started/7_factory_wrap/napi/addon.js
@@ -0,0 +1,11 @@+var createObject = require('bindings')('addon');++var obj = createObject(10);+console.log( obj.plusOne() ); // 11+console.log( obj.plusOne() ); // 12+console.log( obj.plusOne() ); // 13++var obj2 = createObject(20);+console.log( obj2.plusOne() ); // 21+console.log( obj2.plusOne() ); // 22+console.log( obj2.plusOne() ); // 23
src/1-getting-started/7_factory_wrap/napi/binding.gyp
@@ -0,0 +1,8 @@+{+ "targets": [+ {+ "target_name": "addon",+ "sources": [ "addon.cc", "myobject.cc" ]+ }+ ]+}
src/1-getting-started/7_factory_wrap/napi/myobject.cc
@@ -0,0 +1,137 @@+#include "myobject.h"+#include <assert.h>++MyObject::MyObject() : env_(nullptr), wrapper_(nullptr) {}++MyObject::~MyObject() {+ napi_delete_reference(env_, wrapper_);+}++void MyObject::Destructor(napi_env env,+ void* nativeObject,+ void* /*finalize_hint*/) {+ reinterpret_cast<MyObject*>(nativeObject)->~MyObject();+}++#define DECLARE_NAPI_METHOD(name, func) \+ { name, 0, func, 0, 0, 0, napi_default, 0 }++napi_status MyObject::Init(napi_env env) {+ napi_status status;+ napi_property_descriptor properties[] = {+ DECLARE_NAPI_METHOD("plusOne", PlusOne),+ };++ napi_value cons;+ status = napi_define_class(+ env, "MyObject", NAPI_AUTO_LENGTH, New, nullptr, 1, properties, &cons);+ if (status != napi_ok) return status;++ // We will need the constructor `cons` later during the life cycle of the+ // addon, so we store a persistent reference to it as the instance data for+ // our addon. This will enable us to use `napi_get_instance_data` at any+ // point during the life cycle of our addon to retrieve it. We cannot simply+ // store it as a global static variable, because that will render our addon+ // unable to support Node.js worker threads and multiple contexts on a single+ // thread.+ //+ // The finalizer we pass as a lambda will be called when our addon is unloaded+ // and is responsible for releasing the persistent reference and freeing the+ // heap memory where we stored the persistent reference.+ napi_ref* constructor = new napi_ref;+ status = napi_create_reference(env, cons, 1, constructor);+ assert(status == napi_ok);+ status = napi_set_instance_data(+ env,+ constructor,+ [](napi_env env, void* data, void* hint) {+ napi_ref* constructor = static_cast<napi_ref*>(data);+ napi_status status = napi_delete_reference(env, *constructor);+ assert(status == napi_ok);+ delete constructor;+ },+ nullptr);+ assert(status == napi_ok);++ return napi_ok;+}++napi_value MyObject::New(napi_env env, napi_callback_info info) {+ napi_status status;++ size_t argc = 1;+ napi_value args[1];+ napi_value jsthis;+ status = napi_get_cb_info(env, info, &argc, args, &jsthis, nullptr);+ assert(status == napi_ok);++ napi_valuetype valuetype;+ status = napi_typeof(env, args[0], &valuetype);+ assert(status == napi_ok);++ MyObject* obj = new MyObject();++ if (valuetype == napi_undefined) {+ obj->counter_ = 0;+ } else {+ status = napi_get_value_double(env, args[0], &obj->counter_);+ assert(status == napi_ok);+ }++ obj->env_ = env;+ status = napi_wrap(env,+ jsthis,+ reinterpret_cast<void*>(obj),+ MyObject::Destructor,+ nullptr, /* finalize_hint */+ &obj->wrapper_);+ assert(status == napi_ok);++ return jsthis;+}++napi_value MyObject::Constructor(napi_env env) {+ void* instance_data = nullptr;+ napi_status status = napi_get_instance_data(env, &instance_data);+ assert(status == napi_ok);+ napi_ref* constructor = static_cast<napi_ref*>(instance_data);++ napi_value cons;+ status = napi_get_reference_value(env, *constructor, &cons);+ assert(status == napi_ok);+ return cons;+}++napi_status MyObject::NewInstance(napi_env env,+ napi_value arg,+ napi_value* instance) {+ napi_status status;++ const int argc = 1;+ napi_value argv[argc] = {arg};++ status = napi_new_instance(env, Constructor(env), argc, argv, instance);+ if (status != napi_ok) return status;++ return napi_ok;+}++napi_value MyObject::PlusOne(napi_env env, napi_callback_info info) {+ napi_status status;++ napi_value jsthis;+ status = napi_get_cb_info(env, info, nullptr, nullptr, &jsthis, nullptr);+ assert(status == napi_ok);++ MyObject* obj;+ status = napi_unwrap(env, jsthis, reinterpret_cast<void**>(&obj));+ assert(status == napi_ok);++ obj->counter_ += 1;++ napi_value num;+ status = napi_create_double(env, obj->counter_, &num);+ assert(status == napi_ok);++ return num;+}
src/1-getting-started/7_factory_wrap/napi/myobject.h
@@ -0,0 +1,26 @@+#ifndef TEST_ADDONS_NAPI_7_FACTORY_WRAP_MYOBJECT_H_+#define TEST_ADDONS_NAPI_7_FACTORY_WRAP_MYOBJECT_H_++#include <node_api.h>++class MyObject {+ public:+ static napi_status Init(napi_env env);+ static void Destructor(napi_env env, void* nativeObject, void* finalize_hint);+ static napi_status NewInstance(napi_env env,+ napi_value arg,+ napi_value* instance);++ private:+ MyObject();+ ~MyObject();++ static inline napi_value Constructor(napi_env env);+ static napi_value New(napi_env env, napi_callback_info info);+ static napi_value PlusOne(napi_env env, napi_callback_info info);+ double counter_;+ napi_env env_;+ napi_ref wrapper_;+};++#endif // TEST_ADDONS_NAPI_7_FACTORY_WRAP_MYOBJECT_H_
src/1-getting-started/7_factory_wrap/napi/package.json
@@ -0,0 +1,11 @@+{+ "name": "factory_wrap",+ "version": "0.0.0",+ "description": "Node.js Addons Example #7",+ "main": "addon.js",+ "private": true,+ "gypfile": true,+ "dependencies": {+ "bindings": "~1.5.0"+ }+}
src/1-getting-started/7_factory_wrap/node-addon-api/addon.cc
@@ -0,0 +1,14 @@+#include <napi.h>+#include "myobject.h"++Napi::Object CreateObject(const Napi::CallbackInfo& info) {+ return MyObject::NewInstance(info.Env(), info[0]);+}++Napi::Object InitAll(Napi::Env env, Napi::Object exports) {+ Napi::Object new_exports =+ Napi::Function::New(env, CreateObject, "CreateObject");+ return MyObject::Init(env, new_exports);+}++NODE_API_MODULE(addon, InitAll)
src/1-getting-started/7_factory_wrap/node-addon-api/addon.js
@@ -0,0 +1,11 @@+var createObject = require('bindings')('addon');++var obj = createObject(10);+console.log( obj.plusOne() ); // 11+console.log( obj.plusOne() ); // 12+console.log( obj.plusOne() ); // 13++var obj2 = createObject(20);+console.log( obj2.plusOne() ); // 21+console.log( obj2.plusOne() ); // 22+console.log( obj2.plusOne() ); // 23
src/1-getting-started/7_factory_wrap/node-addon-api/binding.gyp
@@ -0,0 +1,14 @@+{+ "targets": [+ {+ "target_name": "addon",+ "cflags!": [ "-fno-exceptions" ],+ "cflags_cc!": [ "-fno-exceptions" ],+ "sources": [ "addon.cc", "myobject.cc" ],+ "include_dirs": [+ "<!@(node -p \"require('node-addon-api').include\")"+ ],+ "defines": [ 'NAPI_DISABLE_CPP_EXCEPTIONS' ],+ }+ ]+}
src/1-getting-started/7_factory_wrap/node-addon-api/myobject.cc
@@ -0,0 +1,35 @@+#include "myobject.h"+#include <napi.h>+#include <uv.h>++using namespace Napi;++Napi::Object MyObject::Init(Napi::Env env, Napi::Object exports) {+ Napi::Function func = DefineClass(+ env, "MyObject", {InstanceMethod("plusOne", &MyObject::PlusOne)});++ Napi::FunctionReference* constructor = new Napi::FunctionReference();+ *constructor = Napi::Persistent(func);+ env.SetInstanceData(constructor);++ exports.Set("MyObject", func);+ return exports;+}++MyObject::MyObject(const Napi::CallbackInfo& info)+ : Napi::ObjectWrap<MyObject>(info) {+ this->counter_ = info[0].As<Napi::Number>().DoubleValue();+};++Napi::Object MyObject::NewInstance(Napi::Env env, Napi::Value arg) {+ Napi::EscapableHandleScope scope(env);+ Napi::Object obj = env.GetInstanceData<Napi::FunctionReference>()->New({arg});+ return scope.Escape(napi_value(obj)).ToObject();+}++Napi::Value MyObject::PlusOne(const Napi::CallbackInfo& info) {+ Napi::Env env = info.Env();+ this->counter_ = this->counter_ + 1;++ return Napi::Number::New(env, this->counter_);+}
src/1-getting-started/7_factory_wrap/node-addon-api/myobject.h
@@ -0,0 +1,17 @@+#ifndef MYOBJECT_H+#define MYOBJECT_H++#include <napi.h>++class MyObject : public Napi::ObjectWrap<MyObject> {+ public:+ static Napi::Object Init(Napi::Env env, Napi::Object exports);+ static Napi::Object NewInstance(Napi::Env env, Napi::Value arg);+ MyObject(const Napi::CallbackInfo& info);++ private:+ Napi::Value PlusOne(const Napi::CallbackInfo& info);+ double counter_;+};++#endif
src/1-getting-started/7_factory_wrap/node-addon-api/package.json
@@ -0,0 +1,15 @@+{+ "name": "factory_wrap",+ "version": "0.0.0",+ "description": "Node.js Addons Example #7",+ "main": "addon.js",+ "private": true,+ "gypfile": true,+ "engines": {+ "node": "~10 >=10.20 || >=12.17"+ },+ "dependencies": {+ "bindings": "~1.2.1",+ "node-addon-api": "^7.0.0"+ }+}
src/1-getting-started/a-first-project/node-addon-api/README.md
@@ -0,0 +1,14 @@+# Node-API A First Project++This is an example project that accompanies the Node-API workshop tutorials++A tutorial describing this project can be found at the [Node-API Resource](https://napi.inspiredware.com/getting-started/first.html).++To build and run this program on your system, clone it to your computer and run these two commands inside your clone:++```+npm install+npm test+```++> You need to have Node 10.5.0 or later installed.
src/1-getting-started/a-first-project/node-addon-api/binding.gyp
@@ -0,0 +1,20 @@+{+ 'targets': [+ {+ 'target_name': 'hello-world-native',+ 'sources': [ 'src/hello_world.cc' ],+ 'include_dirs': ["<!@(node -p \"require('node-addon-api').include\")"],+ 'dependencies': ["<!(node -p \"require('node-addon-api').gyp\")"],+ 'cflags!': [ '-fno-exceptions' ],+ 'cflags_cc!': [ '-fno-exceptions' ],+ 'xcode_settings': {+ 'GCC_ENABLE_CPP_EXCEPTIONS': 'YES',+ 'CLANG_CXX_LIBRARY': 'libc++',+ 'MACOSX_DEPLOYMENT_TARGET': '10.7'+ },+ 'msvs_settings': {+ 'VCCLCompilerTool': { 'ExceptionHandling': 1 },+ }+ }+ ]+}
src/1-getting-started/a-first-project/node-addon-api/lib/binding.js
@@ -0,0 +1,4 @@+const addon = require('../build/Release/hello-world-native');++module.exports = addon.HelloWorld+
src/1-getting-started/a-first-project/node-addon-api/package.json
@@ -0,0 +1,16 @@+{+ "main": "lib/binding.js",+ "private": true,+ "dependencies": {+ "node-addon-api": "^1.1.0"+ },+ "scripts": {+ "test": "node --napi-modules ./test/test_binding.js"+ },+ "gypfile": true,+ "name": "hello-world",+ "version": "1.0.0",+ "description": "A first project.",+ "author": "Your name goes here",+ "license": "ISC"+}
src/1-getting-started/a-first-project/node-addon-api/src/hello_world.cc
@@ -0,0 +1,16 @@+#include <napi.h>++using namespace Napi;++Napi::String Method(const Napi::CallbackInfo& info) {+ Napi::Env env = info.Env();+ return Napi::String::New(env, "world");+}++Napi::Object Init(Napi::Env env, Napi::Object exports) {+ exports.Set(Napi::String::New(env, "HelloWorld"),+ Napi::Function::New(env, Method));+ return exports;+}++NODE_API_MODULE(addon, Init)
src/1-getting-started/a-first-project/node-addon-api/test/test_binding.js
@@ -0,0 +1,14 @@+const HelloWorld = require("../lib/binding.js");+const assert = require("assert");++assert(HelloWorld, "The expected function is undefined");++function testBasic()+{+ const result = HelloWorld("hello");+ assert.strictEqual(result, "world", "Unexpected value returned");+}++assert.doesNotThrow(testBasic, undefined, "testBasic threw an expection");++console.log("Tests passed- everything looks OK!");
src/2-js-to-native-conversion/8_passing_wrapped/nan/addon.cc
@@ -0,0 +1,40 @@+#include <nan.h>+#include "myobject.h"++using namespace v8;++void CreateObject(const Nan::FunctionCallbackInfo<v8::Value>& info) {+ info.GetReturnValue().Set(MyObject::NewInstance(info[0]));+}++void Add(const Nan::FunctionCallbackInfo<v8::Value>& info) {+ v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext();++ MyObject* obj1 = Nan::ObjectWrap::Unwrap<MyObject>(+ info[0]->ToObject(context).ToLocalChecked());+ MyObject* obj2 = Nan::ObjectWrap::Unwrap<MyObject>(+ info[1]->ToObject(context).ToLocalChecked());+ double sum = obj1->Val() + obj2->Val();+ info.GetReturnValue().Set(Nan::New(sum));+}++void InitAll(v8::Local<v8::Object> exports) {+ v8::Local<v8::Context> context =+ exports->GetCreationContext().ToLocalChecked();++ MyObject::Init();++ exports->Set(context,+ Nan::New("createObject").ToLocalChecked(),+ Nan::New<v8::FunctionTemplate>(CreateObject)+ ->GetFunction(context)+ .ToLocalChecked());++ exports->Set(context,+ Nan::New("add").ToLocalChecked(),+ Nan::New<v8::FunctionTemplate>(Add)+ ->GetFunction(context)+ .ToLocalChecked());+}++NODE_MODULE(addon, InitAll)
src/2-js-to-native-conversion/8_passing_wrapped/nan/addon.js
@@ -0,0 +1,7 @@+var addon = require('bindings')('addon');++var obj1 = addon.createObject(10);+var obj2 = addon.createObject(20);+var result = addon.add(obj1, obj2);++console.log(result); // 30
src/2-js-to-native-conversion/8_passing_wrapped/nan/binding.gyp
@@ -0,0 +1,11 @@+{+ "targets": [+ {+ "target_name": "addon",+ "sources": [ "addon.cc", "myobject.cc" ],+ "include_dirs": [+ "<!(node -e \"require('nan')\")"+ ]+ }+ ]+}
src/2-js-to-native-conversion/8_passing_wrapped/nan/myobject.cc
@@ -0,0 +1,43 @@+#include "myobject.h"+#include <node.h>++MyObject::MyObject(){};+MyObject::~MyObject(){};++Nan::Persistent<v8::Function> MyObject::constructor;++void MyObject::Init() {+ Nan::HandleScope scope;++ // Prepare constructor template+ v8::Local<v8::FunctionTemplate> tpl = Nan::New<v8::FunctionTemplate>(New);+ tpl->SetClassName(Nan::New("MyObject").ToLocalChecked());+ tpl->InstanceTemplate()->SetInternalFieldCount(1);++ constructor.Reset(+ tpl->GetFunction(Nan::GetCurrentContext()).ToLocalChecked());+}++void MyObject::New(const Nan::FunctionCallbackInfo<v8::Value>& info) {+ v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext();+ MyObject* obj = new MyObject();+ obj->val_ =+ info[0]->IsUndefined() ? 0 : info[0]->NumberValue(context).FromJust();+ obj->Wrap(info.This());++ info.GetReturnValue().Set(info.This());+}++v8::Local<v8::Object> MyObject::NewInstance(v8::Local<v8::Value> arg) {+ Nan::EscapableHandleScope scope;++ const unsigned argc = 1;+ v8::Local<v8::Value> argv[argc] = {arg};+ v8::Local<v8::Function> cons = Nan::New<v8::Function>(constructor);+ v8::Local<v8::Context> context =+ v8::Isolate::GetCurrent()->GetCurrentContext();+ v8::Local<v8::Object> instance =+ cons->NewInstance(context, argc, argv).ToLocalChecked();++ return scope.Escape(instance);+}
src/2-js-to-native-conversion/8_passing_wrapped/nan/myobject.h
@@ -0,0 +1,21 @@+#ifndef MYOBJECT_H+#define MYOBJECT_H++#include <nan.h>++class MyObject : public Nan::ObjectWrap {+ public:+ static void Init();+ static v8::Local<v8::Object> NewInstance(v8::Local<v8::Value> arg);+ double Val() const { return val_; }++ private:+ MyObject();+ ~MyObject();++ static Nan::Persistent<v8::Function> constructor;+ static void New(const Nan::FunctionCallbackInfo<v8::Value>& info);+ double val_;+};++#endif
src/2-js-to-native-conversion/8_passing_wrapped/nan/package.json
@@ -0,0 +1,12 @@+{+ "name": "passing_wrapped",+ "version": "0.0.0",+ "description": "Node.js Addons Example #8",+ "main": "addon.js",+ "private": true,+ "gypfile": true,+ "dependencies": {+ "bindings": "~1.5.0",+ "nan": "^2.0.0"+ }+}
src/2-js-to-native-conversion/8_passing_wrapped/napi/addon.cc
@@ -0,0 +1,59 @@+#include <assert.h>+#include "myobject.h"++napi_value CreateObject(napi_env env, napi_callback_info info) {+ napi_status status;++ size_t argc = 1;+ napi_value args[1];+ status = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);+ assert(status == napi_ok);++ napi_value instance;+ status = MyObject::NewInstance(env, args[0], &instance);++ return instance;+}++napi_value Add(napi_env env, napi_callback_info info) {+ napi_status status;++ size_t argc = 2;+ napi_value args[2];+ status = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);+ assert(status == napi_ok);++ MyObject* obj1;+ status = napi_unwrap(env, args[0], reinterpret_cast<void**>(&obj1));+ assert(status == napi_ok);++ MyObject* obj2;+ status = napi_unwrap(env, args[1], reinterpret_cast<void**>(&obj2));+ assert(status == napi_ok);++ napi_value sum;+ status = napi_create_double(env, obj1->Val() + obj2->Val(), &sum);+ assert(status == napi_ok);++ return sum;+}++#define DECLARE_NAPI_METHOD(name, func) \+ { name, 0, func, 0, 0, 0, napi_default, 0 }++napi_value Init(napi_env env, napi_value exports) {+ napi_status status;++ MyObject::Init(env);++ napi_property_descriptor desc[] = {+ DECLARE_NAPI_METHOD("createObject", CreateObject),+ DECLARE_NAPI_METHOD("add", Add),+ };+ status =+ napi_define_properties(env, exports, sizeof(desc) / sizeof(*desc), desc);+ assert(status == napi_ok);+ return exports;+}++NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)
src/2-js-to-native-conversion/8_passing_wrapped/napi/addon.js
@@ -0,0 +1,7 @@+var addon = require('bindings')('addon');++var obj1 = addon.createObject(10);+var obj2 = addon.createObject(20);+var result = addon.add(obj1, obj2);++console.log(result); // 30
src/2-js-to-native-conversion/8_passing_wrapped/napi/binding.gyp
@@ -0,0 +1,8 @@+{+ "targets": [+ {+ "target_name": "addon",+ "sources": [ "addon.cc", "myobject.cc" ]+ }+ ]+}
src/2-js-to-native-conversion/8_passing_wrapped/napi/myobject.cc
@@ -0,0 +1,111 @@+#include "myobject.h"+#include <assert.h>++MyObject::MyObject() : env_(nullptr), wrapper_(nullptr) {}++MyObject::~MyObject() {+ napi_delete_reference(env_, wrapper_);+}++void MyObject::Destructor(napi_env env,+ void* nativeObject,+ void* /*finalize_hint*/) {+ reinterpret_cast<MyObject*>(nativeObject)->~MyObject();+}++napi_status MyObject::Init(napi_env env) {+ napi_status status;++ napi_value cons;+ status = napi_define_class(+ env, "MyObject", NAPI_AUTO_LENGTH, New, nullptr, 0, nullptr, &cons);+ if (status != napi_ok) return status;++ // We will need the constructor `cons` later during the life cycle of the+ // application, so we store a persistent reference to it as the instance data+ // for our addon. This will enable us to use `napi_get_instance_data` at any+ // point during the life cycle of our addon to retrieve it. We cannot simply+ // store it as a global static variable, because that will render our addon+ // unable to support Node.js worker threads and multiple contexts on a single+ // thread.+ //+ // The finalizer we pass as a lambda will be called when our addon is unloaded+ // and is responsible for releasing the persistent reference and freeing the+ // heap memory where we stored the persistent reference.+ napi_ref* constructor = new napi_ref;+ status = napi_create_reference(env, cons, 1, constructor);+ assert(status == napi_ok);+ status = napi_set_instance_data(+ env,+ constructor,+ [](napi_env env, void* data, void* hint) {+ napi_ref* constructor = static_cast<napi_ref*>(data);+ napi_status status = napi_delete_reference(env, *constructor);+ assert(status == napi_ok);+ delete constructor;+ },+ nullptr);+ assert(status == napi_ok);++ return napi_ok;+}++napi_value MyObject::New(napi_env env, napi_callback_info info) {+ napi_status status;++ size_t argc = 1;+ napi_value args[1];+ napi_value jsthis;+ status = napi_get_cb_info(env, info, &argc, args, &jsthis, nullptr);+ assert(status == napi_ok);++ MyObject* obj = new MyObject();++ napi_valuetype valuetype;+ status = napi_typeof(env, args[0], &valuetype);+ assert(status == napi_ok);++ if (valuetype == napi_undefined) {+ obj->val_ = 0;+ } else {+ status = napi_get_value_double(env, args[0], &obj->val_);+ assert(status == napi_ok);+ }++ obj->env_ = env;+ status = napi_wrap(env,+ jsthis,+ reinterpret_cast<void*>(obj),+ MyObject::Destructor,+ nullptr, // finalize_hint+ &obj->wrapper_);+ assert(status == napi_ok);++ return jsthis;+}++napi_value MyObject::Constructor(napi_env env) {+ void* instance_data = nullptr;+ napi_status status = napi_get_instance_data(env, &instance_data);+ assert(status == napi_ok);+ napi_ref* constructor = static_cast<napi_ref*>(instance_data);++ napi_value cons;+ status = napi_get_reference_value(env, *constructor, &cons);+ assert(status == napi_ok);+ return cons;+}++napi_status MyObject::NewInstance(napi_env env,+ napi_value arg,+ napi_value* instance) {+ napi_status status;++ const int argc = 1;+ napi_value argv[argc] = {arg};++ status = napi_new_instance(env, Constructor(env), argc, argv, instance);+ if (status != napi_ok) return status;++ return napi_ok;+}
src/2-js-to-native-conversion/8_passing_wrapped/napi/myobject.h
@@ -0,0 +1,26 @@+#ifndef TEST_ADDONS_NAPI_8_PASSING_WRAPPED_MYOBJECT_H_+#define TEST_ADDONS_NAPI_8_PASSING_WRAPPED_MYOBJECT_H_++#include <node_api.h>++class MyObject {+ public:+ static napi_status Init(napi_env env);+ static void Destructor(napi_env env, void* nativeObject, void* finalize_hint);+ static napi_status NewInstance(napi_env env,+ napi_value arg,+ napi_value* instance);+ double Val() const { return val_; }++ private:+ MyObject();+ ~MyObject();++ static inline napi_value Constructor(napi_env env);+ static napi_value New(napi_env env, napi_callback_info info);+ double val_;+ napi_env env_;+ napi_ref wrapper_;+};++#endif // TEST_ADDONS_NAPI_8_PASSING_WRAPPED_MYOBJECT_H_
src/2-js-to-native-conversion/8_passing_wrapped/napi/package.json
@@ -0,0 +1,11 @@+{+ "name": "passing_wrapped",+ "version": "0.0.0",+ "description": "Node.js Addons Example #8",+ "main": "addon.js",+ "private": true,+ "gypfile": true,+ "dependencies": {+ "bindings": "~1.5.0"+ }+}
src/2-js-to-native-conversion/8_passing_wrapped/node-addon-api/addon.cc
@@ -0,0 +1,31 @@+#include <napi.h>+#include "myobject.h"++using namespace Napi;++Napi::Object CreateObject(const Napi::CallbackInfo& info) {+ return MyObject::NewInstance(info.Env(), info[0]);+}++Napi::Number Add(const Napi::CallbackInfo& info) {+ Napi::Env env = info.Env();+ MyObject* obj1 =+ Napi::ObjectWrap<MyObject>::Unwrap(info[0].As<Napi::Object>());+ MyObject* obj2 =+ Napi::ObjectWrap<MyObject>::Unwrap(info[1].As<Napi::Object>());+ double sum = obj1->Val() + obj2->Val();+ return Napi::Number::New(env, sum);+}++Napi::Object InitAll(Napi::Env env, Napi::Object exports) {+ MyObject::Init(env, exports);++ exports.Set(Napi::String::New(env, "createObject"),+ Napi::Function::New(env, CreateObject));++ exports.Set(Napi::String::New(env, "add"), Napi::Function::New(env, Add));++ return exports;+}++NODE_API_MODULE(addon, InitAll)
src/2-js-to-native-conversion/8_passing_wrapped/node-addon-api/addon.js
@@ -0,0 +1,7 @@+var addon = require('bindings')('addon');++var obj1 = addon.createObject(10);+var obj2 = addon.createObject(20);+var result = addon.add(obj1, obj2);++console.log(result); // 30
src/2-js-to-native-conversion/8_passing_wrapped/node-addon-api/binding.gyp
@@ -0,0 +1,16 @@+{+ "targets": [+ {+ "target_name": "addon",+ "cflags!": [ "-fno-exceptions" ],+ "cflags_cc!": [ "-fno-exceptions" ],+ "cflags!": [ "-fno-exceptions" ],+ "cflags_cc!": [ "-fno-exceptions" ],+ "sources": [ "addon.cc", "myobject.cc" ],+ "include_dirs": [+ "<!@(node -p \"require('node-addon-api').include\")"+ ],+ 'defines': [ 'NAPI_DISABLE_CPP_EXCEPTIONS' ],+ }+ ]+}
src/2-js-to-native-conversion/8_passing_wrapped/node-addon-api/myobject.cc
@@ -0,0 +1,25 @@+#include "myobject.h"+#include <napi.h>+#include <uv.h>++MyObject::MyObject(const Napi::CallbackInfo& info)+ : Napi::ObjectWrap<MyObject>(info) {+ this->val_ = info[0].As<Napi::Number>().DoubleValue();+};++void MyObject::Init(Napi::Env env, Napi::Object exports) {+ Napi::Function func = DefineClass(env, "MyObject", {});++ Napi::FunctionReference* constructor = new Napi::FunctionReference();+ *constructor = Napi::Persistent(func);+ env.SetInstanceData(constructor); // NOTE: this assumes only 1 class is+ // exported for multiple exported classes,+ // need a struct or other mechanism++ exports.Set("MyObject", func);+}++Napi::Object MyObject::NewInstance(Napi::Env env, Napi::Value arg) {+ Napi::Object obj = env.GetInstanceData<Napi::FunctionReference>()->New({arg});+ return obj;+}
src/2-js-to-native-conversion/8_passing_wrapped/node-addon-api/myobject.h
@@ -0,0 +1,17 @@+#ifndef MYOBJECT_H+#define MYOBJECT_H++#include <napi.h>++class MyObject : public Napi::ObjectWrap<MyObject> {+ public:+ static void Init(Napi::Env env, Napi::Object exports);+ static Napi::Object NewInstance(Napi::Env env, Napi::Value arg);+ double Val() const { return val_; }+ MyObject(const Napi::CallbackInfo& info);++ private:+ double val_;+};++#endif
src/2-js-to-native-conversion/8_passing_wrapped/node-addon-api/package.json
@@ -0,0 +1,15 @@+{+ "name": "passing_wrapped",+ "version": "0.0.0",+ "description": "Node.js Addons Example #8",+ "main": "addon.js",+ "private": true,+ "gypfile": true,+ "engines": {+ "node": "~10 >=10.20 || >=12.17"+ },+ "dependencies": {+ "bindings": "~1.5.0",+ "node-addon-api": "^3.0.0"+ }+}
src/2-js-to-native-conversion/array_buffer_to_native/node-addon-api/array_buffer_to_native.cc
@@ -0,0 +1,35 @@+#include <napi.h>+#include <stdio.h>++static void ArrayConsumer(const int32_t* array, size_t length) {+ for (size_t index = 0; index < length; index++) {+ fprintf(stderr, "array[%lu] = %d\n", index, array[index]);+ }+}++static Napi::Value AcceptArrayBuffer(const Napi::CallbackInfo& info) {+ if (info.Length() != 1) {+ Napi::Error::New(info.Env(), "Expected exactly one argument")+ .ThrowAsJavaScriptException();+ return info.Env().Undefined();+ }+ if (!info[0].IsArrayBuffer()) {+ Napi::Error::New(info.Env(), "Expected an ArrayBuffer")+ .ThrowAsJavaScriptException();+ return info.Env().Undefined();+ }++ Napi::ArrayBuffer buf = info[0].As<Napi::ArrayBuffer>();++ ArrayConsumer(reinterpret_cast<int32_t*>(buf.Data()),+ buf.ByteLength() / sizeof(int32_t));++ return info.Env().Undefined();+}++static Napi::Object Init(Napi::Env env, Napi::Object exports) {+ exports["AcceptArrayBuffer"] = Napi::Function::New(env, AcceptArrayBuffer);+ return exports;+}++NODE_API_MODULE(NODE_GYP_MODULE_NAME, Init)
src/2-js-to-native-conversion/array_buffer_to_native/node-addon-api/binding.gyp
@@ -0,0 +1,14 @@+{+ "targets": [+ {+ "target_name": "array_buffer_to_native",+ "cflags!": [ "-fno-exceptions" ],+ "cflags_cc!": [ "-fno-exceptions" ],+ "sources": [ "array_buffer_to_native.cc" ],+ "include_dirs": [+ "<!@(node -p \"require('node-addon-api').include\")"+ ],+ 'defines': [ 'NAPI_DISABLE_CPP_EXCEPTIONS' ],+ }+ ]+}
src/2-js-to-native-conversion/array_buffer_to_native/node-addon-api/index.js
@@ -0,0 +1,15 @@+const binding = require('bindings')('array_buffer_to_native');+const array = new Int32Array(10);++array[0] = 19;+array[1] = -41;+array[2] = 98;+array[3] = -922;+array[4] = 587;+array[5] = 12;+array[6] = 221;+array[7] = 49;+array[8] = -96;+array[9] = -1;++binding.AcceptArrayBuffer(array.buffer);
src/2-js-to-native-conversion/array_buffer_to_native/node-addon-api/package.json
@@ -0,0 +1,14 @@+{+ "name": "array_buffer_to_native",+ "version": "0.0.0",+ "description": "Fill Out a Native Array in JavaScript",+ "main": "index.js",+ "private": true,+ "dependencies": {+ "bindings": "~1.2.1",+ "node-addon-api": "^1.0.0"+ },+ "scripts": {+ "test": "node index.js"+ }+}
src/2-js-to-native-conversion/object-template-demo/README.md
@@ -0,0 +1,3 @@+The goal of this demo is to show how to implement V8 ObjectTemplate-like handlers.+NAN uses the ObjectTemplate directly: the code is adapted from NAN namedinterceptors unit test.+Node-API cannot use the ObjectTemplate: it uses the JavaScript Proxy object to get similar behavior.
src/2-js-to-native-conversion/object-template-demo/nan/binding.gyp
@@ -0,0 +1,11 @@+{+ "targets": [+ {+ "target_name": "object-template-demo",+ "sources": [ "object-template-demo.cc" ],+ "include_dirs": [+ "<!(node -e \"require('nan')\")"+ ]+ }+ ]+}
src/2-js-to-native-conversion/object-template-demo/nan/index.js
@@ -0,0 +1,10 @@+const addon = require('bindings')('object-template-demo');++const interceptor = addon.create();+console.log(interceptor.prop); // 'foo'+interceptor.prop = 'setting a value';+console.log(interceptor.prop); // 'setting a value'+delete interceptor.something;+console.log(interceptor.prop); // 'goober';+console.log(Object.prototype.hasOwnProperty.call(interceptor, "thing")); // true+console.log(Object.keys(interceptor)[0]); // 'value'
src/2-js-to-native-conversion/object-template-demo/nan/object-template-demo.cc
@@ -0,0 +1,118 @@+/*********************************************************************+ * NAN - Native Abstractions for Node.js+ *+ * Copyright (c) 2018 NAN contributors+ *+ * MIT License <https://github.com/nodejs/nan/blob/master/LICENSE.md>+ ********************************************************************/++#include <nan.h>+#include <cstring>++using namespace Nan; // NOLINT(build/namespaces)++class NamedInterceptor : public ObjectWrap {+ char buf[256];++ public:+ NamedInterceptor() { std::strncpy(this->buf, "foo", sizeof(this->buf)); }+ static NAN_MODULE_INIT(Init);+ static v8::Local<v8::Value> NewInstance();+ static NAN_METHOD(New);++ static NAN_PROPERTY_GETTER(PropertyGetter);+ static NAN_PROPERTY_SETTER(PropertySetter);+ static NAN_PROPERTY_ENUMERATOR(PropertyEnumerator);+ static NAN_PROPERTY_DELETER(PropertyDeleter);+ static NAN_PROPERTY_QUERY(PropertyQuery);+};++static Persistent<v8::FunctionTemplate> namedinterceptors_constructor;++NAN_METHOD(CreateNew) {+ info.GetReturnValue().Set(NamedInterceptor::NewInstance());+}++NAN_MODULE_INIT(NamedInterceptor::Init) {+ v8::Local<v8::FunctionTemplate> tpl =+ Nan::New<v8::FunctionTemplate>(NamedInterceptor::New);+ namedinterceptors_constructor.Reset(tpl);+ tpl->SetClassName(Nan::New("NamedInterceptor").ToLocalChecked());+ tpl->InstanceTemplate()->SetInternalFieldCount(1);+ v8::Local<v8::ObjectTemplate> inst = tpl->InstanceTemplate();++ SetNamedPropertyHandler(inst,+ NamedInterceptor::PropertyGetter,+ NamedInterceptor::PropertySetter,+ NamedInterceptor::PropertyQuery,+ NamedInterceptor::PropertyDeleter,+ NamedInterceptor::PropertyEnumerator);++ v8::Local<v8::Function> createnew =+ Nan::GetFunction(Nan::New<v8::FunctionTemplate>(CreateNew))+ .ToLocalChecked();+ Set(target, Nan::New("create").ToLocalChecked(), createnew);+}++v8::Local<v8::Value> NamedInterceptor::NewInstance() {+ EscapableHandleScope scope;+ v8::Local<v8::FunctionTemplate> constructorHandle =+ Nan::New(namedinterceptors_constructor);+ v8::Local<v8::Object> instance =+ Nan::NewInstance(GetFunction(constructorHandle).ToLocalChecked())+ .ToLocalChecked();+ return scope.Escape(instance);+}++NAN_METHOD(NamedInterceptor::New) {+ NamedInterceptor* interceptor = new NamedInterceptor();+ interceptor->Wrap(info.This());+ info.GetReturnValue().Set(info.This());+}++NAN_PROPERTY_GETTER(NamedInterceptor::PropertyGetter) {+ NamedInterceptor* interceptor =+ ObjectWrap::Unwrap<NamedInterceptor>(info.Holder());+ if (!std::strcmp(*Nan::Utf8String(property), "prop")) {+ info.GetReturnValue().Set(Nan::New(interceptor->buf).ToLocalChecked());+ } else {+ info.GetReturnValue().Set(Nan::New("bar").ToLocalChecked());+ }+}++NAN_PROPERTY_SETTER(NamedInterceptor::PropertySetter) {+ NamedInterceptor* interceptor =+ ObjectWrap::Unwrap<NamedInterceptor>(info.Holder());+ if (!std::strcmp(*Nan::Utf8String(property), "prop")) {+ std::strncpy(+ interceptor->buf, *Nan::Utf8String(value), sizeof(interceptor->buf));+ info.GetReturnValue().Set(info.This());+ } else {+ info.GetReturnValue().Set(info.This());+ }+}++NAN_PROPERTY_ENUMERATOR(NamedInterceptor::PropertyEnumerator) {+ v8::Local<v8::Array> arr = Nan::New<v8::Array>();+ Set(arr, 0, Nan::New("value").ToLocalChecked());+ info.GetReturnValue().Set(arr);+}++NAN_PROPERTY_DELETER(NamedInterceptor::PropertyDeleter) {+ NamedInterceptor* interceptor =+ ObjectWrap::Unwrap<NamedInterceptor>(info.Holder());+ std::strncpy(interceptor->buf, "goober", sizeof(interceptor->buf));+ info.GetReturnValue().Set(True());+}++NAN_PROPERTY_QUERY(NamedInterceptor::PropertyQuery) {+ Nan::Utf8String s(property);+ if (!std::strcmp(*s, "thing")) {+ return info.GetReturnValue().Set(Nan::New<v8::Integer>(v8::DontEnum));+ }+ if (!std::strcmp(*s, "value")) {+ return info.GetReturnValue().Set(Nan::New(0));+ }+}++NODE_MODULE(namedinterceptors, NamedInterceptor::Init)
src/2-js-to-native-conversion/object-template-demo/nan/package.json
@@ -0,0 +1,15 @@+{+ "name": "object-template-demo",+ "version": "0.0.0",+ "description": "Intercept named property access using V8 ObjectTemplate",+ "main": "index.js",+ "private": true,+ "gypfile": true,+ "scripts": {+ "test": "node index.js"+ },+ "dependencies": {+ "bindings": "~1.5.0",+ "nan": "^2.14.0"+ }+}
src/2-js-to-native-conversion/object-template-demo/napi/binding.gyp
@@ -0,0 +1,8 @@+{+ "targets": [+ {+ "target_name": "object_template_demo",+ "sources": [ "object-template-demo.cc", "proxy-template.cc" ]+ }+ ]+}
src/2-js-to-native-conversion/object-template-demo/napi/index.js
@@ -0,0 +1,10 @@+const addon = require('bindings')('object_template_demo');++const interceptor = addon.create();+console.log(interceptor.prop); // 'foo'+interceptor.prop = 'setting a value';+console.log(interceptor.prop); // 'setting a value'+delete interceptor.something;+console.log(interceptor.prop); // 'goober';+console.log(Object.prototype.hasOwnProperty.call(interceptor, "thing")); // true+console.log(Object.keys(interceptor)[0]); // 'value'
src/2-js-to-native-conversion/object-template-demo/napi/node-api-common.h
@@ -0,0 +1,141 @@+#include <node_api.h>+#include <initializer_list>+#include <utility>++// Empty value so that macros here are able to return NULL or void+#define NODE_API_RETVAL_NOTHING // Intentionally blank #define++#define GET_AND_THROW_LAST_ERROR(env) \+ do { \+ const napi_extended_error_info* error_info; \+ napi_get_last_error_info((env), &error_info); \+ bool is_pending; \+ const char* err_message = error_info->error_message; \+ napi_is_exception_pending((env), &is_pending); \+ /* If an exception is already pending, don't rethrow it */ \+ if (!is_pending) { \+ const char* error_message = \+ err_message != NULL ? err_message : "empty error message"; \+ napi_throw_error((env), NULL, error_message); \+ } \+ } while (0)++#define NODE_API_ASSERT_BASE(env, assertion, message, ret_val) \+ do { \+ if (!(assertion)) { \+ napi_throw_error( \+ (env), NULL, "assertion (" #assertion ") failed: " message); \+ return ret_val; \+ } \+ } while (0)++// Returns NULL on failed assertion.+// This is meant to be used inside napi_callback methods.+#define NODE_API_ASSERT(env, assertion, message) \+ NODE_API_ASSERT_BASE(env, assertion, message, NULL)++#define NODE_API_CALL_BASE(env, the_call, ret_val) \+ do { \+ if ((the_call) != napi_ok) { \+ GET_AND_THROW_LAST_ERROR((env)); \+ return ret_val; \+ } \+ } while (0)++// Returns NULL if the_call doesn't return napi_ok.+#define NODE_API_CALL(env, the_call) NODE_API_CALL_BASE(env, the_call, NULL)++// Returns empty if the_call doesn't return napi_ok.+#define NODE_API_CALL_RETURN_VOID(env, the_call) \+ NODE_API_CALL_BASE(env, the_call, NODE_API_RETVAL_NOTHING)++#define CHECK_NAPI(...) \+ do { \+ napi_status res__ = (__VA_ARGS__); \+ if (res__ != napi_ok) { \+ return res__; \+ } \+ } while (0)++#define NAPI_CALL(expr) NODE_API_CALL(env, expr);++#ifdef __cpp_lib_span+#include <span>+using std::span;+#else+/**+ * @brief A span of values that can be used to pass arguments to function.+ *+ * For C++20 we should consider to replace it with std::span.+ */+template <typename T>+struct span {+ constexpr span(std::initializer_list<T> il) noexcept+ : data_{const_cast<T*>(il.begin())}, size_{il.size()} {}+ constexpr span(T* data, size_t size) noexcept : data_{data}, size_{size} {}+ [[nodiscard]] constexpr T* data() const noexcept { return data_; }+ [[nodiscard]] constexpr size_t size() const noexcept { return size_; }+ [[nodiscard]] constexpr T* begin() const noexcept { return data_; }+ [[nodiscard]] constexpr T* end() const noexcept { return data_ + size_; }+ const T& operator[](size_t index) const noexcept { return *(data_ + index); }++ private:+ T* data_;+ size_t size_;+};+#endif // __cpp_lib_span++struct RefHolder {+ RefHolder(std::nullptr_t = nullptr) noexcept {}+ explicit RefHolder(napi_env env, napi_value value) : env_(env) {+ // Start with 2 to avoid ever going to 0 that creates a weak ref.+ napi_create_reference(env, value, 2, &ref_);+ }++ // The class is movable.+ RefHolder(RefHolder&& other) noexcept+ : env_(std::exchange(other.env_, nullptr)),+ ref_(std::exchange(other.ref_, nullptr)) {}++ RefHolder& operator=(RefHolder&& other) noexcept {+ if (this != &other) {+ swap(*this, other);+ RefHolder temp(std::move(other));+ }+ return *this;+ }++ // The class is not copyable.+ RefHolder(const RefHolder& other) = delete;+ RefHolder& operator=(const RefHolder& other) = delete;++ ~RefHolder() noexcept {+ if (env_ != nullptr && ref_ != nullptr) {+ uint32_t refCount{};+ napi_reference_unref(env_, ref_, &refCount);+ if (refCount == 1) {+ napi_delete_reference(env_, ref_);+ }+ }+ }++ operator napi_value() const {+ napi_value result{};+ if (ref_ != nullptr) {+ napi_get_reference_value(env_, ref_, &result);+ }+ return result;+ }++ explicit operator bool() const noexcept { return ref_ != nullptr; }++ friend void swap(RefHolder& left, RefHolder& right) noexcept {+ using std::swap;+ swap(left.env_, right.env_);+ swap(left.ref_, right.ref_);+ }++ private:+ napi_env env_{};+ napi_ref ref_{};+};
src/2-js-to-native-conversion/object-template-demo/napi/object-template-demo.cc
@@ -0,0 +1,306 @@+#include <cstring>+#include <memory>+#include <string>+#include "proxy-template.h"++struct InstanceData {+ RefHolder constructor_;+ std::unique_ptr<ProxyTemplate> proxyTemplate_;+};++class NamedInterceptor {+ char buf[256];++ public:+ NamedInterceptor() { std::strncpy(this->buf, "foo", sizeof(this->buf)); }+ static napi_value Init(napi_env env, napi_value exports);+ static napi_value Constructor(napi_env env);+ static napi_value NewInstance(napi_env env);+ static napi_value New(napi_env env, napi_callback_info info);+ static napi_value CreateNew(napi_env env, napi_callback_info info);++ static napi_status ToUtf8String(napi_env env,+ napi_value value,+ std::string* result);+ static napi_status PropertyGetter(napi_env env,+ napi_value target,+ napi_value key,+ napi_value receiver,+ napi_value* result);+ static napi_status PropertySetter(napi_env env,+ napi_value target,+ napi_value key,+ napi_value value,+ napi_value receiver,+ bool* result);+ static napi_status PropertyDeleter(napi_env env,+ napi_value target,+ napi_value key,+ bool* result);+ static napi_status PropertyQuery(napi_env env,+ napi_value target,+ napi_value key,+ bool* result);+ static napi_status GetOwnPropertyDescriptor(napi_env env,+ napi_value target,+ napi_value key,+ napi_value* result);+ static napi_status OwnKeys(napi_env env,+ napi_value target,+ napi_value* result);+};++napi_value NamedInterceptor::CreateNew(napi_env env, napi_callback_info info) {+ return NamedInterceptor::NewInstance(env);+}++napi_value NamedInterceptor::Init(napi_env env, napi_value exports) {+ napi_value constructor{};+ NAPI_CALL(napi_define_class(env,+ "NamedInterceptor",+ NAPI_AUTO_LENGTH,+ NamedInterceptor::New,+ nullptr,+ 0,+ nullptr,+ &constructor));++ ProxyHandlerBuilder handlerBuilder{};+ NAPI_CALL(handlerBuilder.Get(env, &PropertyGetter));+ NAPI_CALL(handlerBuilder.Set(env, &PropertySetter));+ NAPI_CALL(handlerBuilder.DeleteProperty(env, &PropertyDeleter));+ NAPI_CALL(handlerBuilder.Has(env, &PropertyQuery));+ NAPI_CALL(+ handlerBuilder.GetOwnPropertyDescriptor(env, &GetOwnPropertyDescriptor));+ NAPI_CALL(handlerBuilder.OwnKeys(env, &OwnKeys));+ napi_value proxyHandler{};+ NAPI_CALL(handlerBuilder.NewHandler(env, &proxyHandler));+ NAPI_CALL(napi_set_instance_data(+ env,+ new InstanceData{RefHolder(env, constructor),+ std::make_unique<ProxyTemplate>(env, proxyHandler)},+ [](napi_env /*env*/, void* data, void* /*hint*/) {+ delete static_cast<InstanceData*>(data);+ },+ nullptr));++ napi_value createFunc{};+ NAPI_CALL(napi_create_function(+ env, "create", NAPI_AUTO_LENGTH, CreateNew, nullptr, &createFunc));++ NAPI_CALL(napi_set_named_property(env, exports, "create", createFunc));+ return exports;+}++napi_value NamedInterceptor::Constructor(napi_env env) {+ InstanceData* instanceData{};+ NAPI_CALL(+ napi_get_instance_data(env, reinterpret_cast<void**>(&instanceData)));+ return instanceData->constructor_;+}++napi_value NamedInterceptor::NewInstance(napi_env env) {+ napi_escapable_handle_scope scope{};+ NAPI_CALL(napi_open_escapable_handle_scope(env, &scope));++ napi_value instance{};+ NAPI_CALL(napi_new_instance(env, Constructor(env), 0, nullptr, &instance));++ napi_value result{};+ NAPI_CALL(napi_escape_handle(env, scope, instance, &result));+ NAPI_CALL(napi_close_escapable_handle_scope(env, scope));+ return result;+}++napi_value NamedInterceptor::New(napi_env env, napi_callback_info info) {+ napi_value newTarget{};+ NAPI_CALL(napi_get_new_target(env, info, &newTarget));+ NODE_API_ASSERT_BASE(+ env, newTarget != nullptr, "Must be invoked as a constructor.", nullptr);++ napi_value proxyTarget{}, result{};+ NAPI_CALL(napi_create_function(+ env,+ "target",+ NAPI_AUTO_LENGTH,+ [](napi_env, napi_callback_info) -> napi_value { return nullptr; },+ nullptr,+ &proxyTarget));++ NamedInterceptor* obj = new NamedInterceptor();++ NAPI_CALL(napi_wrap(+ env,+ proxyTarget,+ obj,+ [](napi_env /*env*/, void* data, void* /*hint*/) {+ delete reinterpret_cast<NamedInterceptor*>(data);+ },+ nullptr,+ nullptr));++ InstanceData* instanceData{};+ NAPI_CALL(+ napi_get_instance_data(env, reinterpret_cast<void**>(&instanceData)));+ NAPI_CALL(+ instanceData->proxyTemplate_->NewInstance(env, proxyTarget, &result));+ return result;+}++napi_status NamedInterceptor::ToUtf8String(napi_env env,+ napi_value value,+ std::string* result) {+ size_t size{};+ CHECK_NAPI(napi_get_value_string_utf8(env, value, nullptr, 0, &size));+ result->assign(size, ' ');+ return napi_get_value_string_utf8(+ env, value, &(*result)[0], size + 1, nullptr);+}++napi_status NamedInterceptor::PropertyGetter(napi_env env,+ napi_value target,+ napi_value key,+ napi_value receiver,+ napi_value* result) {+ NamedInterceptor* interceptor{};+ CHECK_NAPI(napi_unwrap(env, target, reinterpret_cast<void**>(&interceptor)));+ std::string keyStr;+ CHECK_NAPI(ToUtf8String(env, key, &keyStr));+ if (keyStr == "prop") {+ return napi_create_string_utf8(+ env, interceptor->buf, NAPI_AUTO_LENGTH, result);+ } else {+ return napi_create_string_utf8(env, "bar", NAPI_AUTO_LENGTH, result);+ }+}++napi_status NamedInterceptor::PropertySetter(napi_env env,+ napi_value target,+ napi_value key,+ napi_value value,+ napi_value receiver,+ bool* result) {+ NamedInterceptor* interceptor{};+ CHECK_NAPI(napi_unwrap(env, target, reinterpret_cast<void**>(&interceptor)));+ std::string keyStr;+ CHECK_NAPI(ToUtf8String(env, key, &keyStr));+ if (keyStr == "prop") {+ std::string valueStr;+ CHECK_NAPI(ToUtf8String(env, value, &valueStr));+ std::strncpy(interceptor->buf, valueStr.data(), sizeof(interceptor->buf));+ *result = true;+ } else {+ *result = false;+ }+ return napi_ok;+}++napi_status NamedInterceptor::PropertyDeleter(napi_env env,+ napi_value target,+ napi_value key,+ bool* result) {+ NamedInterceptor* interceptor{};+ CHECK_NAPI(napi_unwrap(env, target, reinterpret_cast<void**>(&interceptor)));+ std::strncpy(interceptor->buf, "goober", sizeof(interceptor->buf));+ *result = true;+ return napi_ok;+}++napi_status NamedInterceptor::PropertyQuery(napi_env env,+ napi_value target,+ napi_value key,+ bool* result) {+ std::string keyStr;+ CHECK_NAPI(ToUtf8String(env, key, &keyStr));++ if (keyStr == "thing") {+ *result = true;+ }+ if (keyStr == "value") {+ *result = true;+ }+ return napi_ok;+}++napi_status NamedInterceptor::GetOwnPropertyDescriptor(napi_env env,+ napi_value target,+ napi_value key,+ napi_value* result) {+ std::string keyStr;+ CHECK_NAPI(ToUtf8String(env, key, &keyStr));++ if (keyStr == "thing") {+ CHECK_NAPI(napi_create_object(env, result));+ napi_value trueValue{};+ CHECK_NAPI(napi_get_boolean(env, true, &trueValue));+ CHECK_NAPI(+ napi_set_named_property(env, *result, "configurable", trueValue));+ CHECK_NAPI(napi_set_named_property(env, *result, "writable", trueValue));+ }+ if (keyStr == "value") {+ CHECK_NAPI(napi_create_object(env, result));+ napi_value trueValue{};+ CHECK_NAPI(napi_get_boolean(env, true, &trueValue));+ CHECK_NAPI(+ napi_set_named_property(env, *result, "configurable", trueValue));+ CHECK_NAPI(napi_set_named_property(env, *result, "writable", trueValue));+ CHECK_NAPI(napi_set_named_property(env, *result, "enumerable", trueValue));+ }+ if (keyStr == "arguments" || keyStr == "caller") {+ CHECK_NAPI(napi_create_object(env, result));+ napi_value trueValue{}, falseValue{};+ CHECK_NAPI(napi_get_boolean(env, false, &falseValue));+ CHECK_NAPI(napi_get_boolean(env, false, &trueValue));+ CHECK_NAPI(+ napi_set_named_property(env, *result, "configurable", falseValue));+ CHECK_NAPI(napi_set_named_property(env, *result, "writable", falseValue));+ CHECK_NAPI(napi_set_named_property(env, *result, "enumerable", falseValue));+ napi_value nullValue{};+ CHECK_NAPI(napi_get_null(env, &nullValue));+ CHECK_NAPI(napi_set_named_property(env, *result, "value", nullValue));+ }+ if (keyStr == "prototype") {+ CHECK_NAPI(napi_create_object(env, result));+ napi_value trueValue{}, falseValue{};+ CHECK_NAPI(napi_get_boolean(env, false, &falseValue));+ CHECK_NAPI(napi_get_boolean(env, true, &trueValue));+ CHECK_NAPI(+ napi_set_named_property(env, *result, "configurable", falseValue));+ CHECK_NAPI(napi_set_named_property(env, *result, "writable", trueValue));+ CHECK_NAPI(napi_set_named_property(env, *result, "enumerable", falseValue));+ napi_value nullValue{};+ CHECK_NAPI(napi_get_null(env, &nullValue));+ CHECK_NAPI(napi_set_named_property(env, *result, "value", nullValue));+ }+ return napi_ok;+}++napi_status NamedInterceptor::OwnKeys(napi_env env,+ napi_value target,+ napi_value* result) {+ CHECK_NAPI(napi_get_all_property_names(env,+ target,+ napi_key_own_only,+ napi_key_all_properties,+ napi_key_keep_numbers,+ result));++ uint32_t arraySize{};+ CHECK_NAPI(napi_get_array_length(env, *result, &arraySize));+ napi_value firstElement{};+ CHECK_NAPI(napi_get_element(env, *result, 0, &firstElement));+ CHECK_NAPI(napi_set_element(env, *result, arraySize, firstElement));+ napi_value arraySizeValue{};+ CHECK_NAPI(napi_create_uint32(env, arraySize, &arraySizeValue));+ CHECK_NAPI(napi_set_named_property(env, *result, "length", arraySizeValue));+ napi_value value{};+ CHECK_NAPI(napi_create_string_utf8(env, "value", NAPI_AUTO_LENGTH, &value));+ CHECK_NAPI(napi_set_element(env, *result, 0, value));+ return napi_ok;+}++extern "C" napi_value Init(napi_env env, napi_value exports) {+ return NamedInterceptor::Init(env, exports);+}++NAPI_MODULE(NODE_GYP_MODULE_NAME, Init);
src/2-js-to-native-conversion/object-template-demo/napi/package.json
@@ -0,0 +1,14 @@+{+ "name": "object_template_demo",+ "version": "0.0.0",+ "description": "Intercept named property access using Proxy",+ "main": "index.js",+ "private": true,+ "gypfile": true,+ "scripts": {+ "test": "node index.js"+ },+ "dependencies": {+ "bindings": "~1.5.0"+ }+}
src/2-js-to-native-conversion/object-template-demo/napi/proxy-template.cc
@@ -0,0 +1,726 @@+#include "proxy-template.h"+#include <vector>++namespace {++static napi_status SetTrap(napi_env env,+ napi_value handler,+ const char* propertyName,+ napi_value trap) noexcept {+ if (trap != nullptr) {+ CHECK_NAPI(napi_set_named_property(env, handler, propertyName, trap));+ }+ return napi_ok;+}++using TrapCaller = napi_status (*)(napi_env env,+ void* trap,+ napi_span<napi_value> args,+ napi_value* result);++struct TrapCallInfo {+ TrapCaller Caller;+ void* trap;+};++template <size_t argCount>+napi_callback CreateTrapCallback() noexcept {+ return [](napi_env env, napi_callback_info info) noexcept -> napi_value {+ TrapCallInfo* trapCallInfo{};+ napi_value args[argCount]{};+ size_t actualArgCount{argCount};+ NAPI_CALL(napi_get_cb_info(env,+ info,+ &actualArgCount,+ args,+ nullptr,+ reinterpret_cast<void**>(&trapCallInfo)));+ NODE_API_ASSERT_BASE(env,+ actualArgCount == argCount,+ "proxy trap requires argCount arguments.",+ nullptr);+ napi_value result{};+ NAPI_CALL(trapCallInfo->Caller(+ env, trapCallInfo->trap, span<napi_value>(args, argCount), &result));+ return result;+ };+}++napi_status ToVector(napi_env env,+ napi_value arrayValue,+ std::vector<napi_value>* result) {+ bool isArray{};+ CHECK_NAPI(napi_is_array(env, arrayValue, &isArray));+ if (isArray) {+ uint32_t arraySize{};+ CHECK_NAPI(napi_get_array_length(env, arrayValue, &arraySize));+ result->reserve(result->size() + arraySize);+ for (uint32_t i = 0; i < arraySize; ++i) {+ napi_value arrayElement{};+ CHECK_NAPI(napi_get_element(env, arrayValue, i, &arrayElement));+ result->push_back(arrayElement);+ }+ }+ return napi_ok;+}++} // namespace++napi_status ProxyHandlerBuilder::NewHandler(napi_env env,+ napi_value* result) noexcept {+ CHECK_NAPI(napi_create_object(env, result));++ CHECK_NAPI(SetTrap(env, *result, "apply", apply_));+ CHECK_NAPI(SetTrap(env, *result, "construct", construct_));+ CHECK_NAPI(SetTrap(env, *result, "defineProperty", defineProperty_));+ CHECK_NAPI(SetTrap(env, *result, "deleteProperty", deleteProperty_));+ CHECK_NAPI(SetTrap(env, *result, "get", get_));+ CHECK_NAPI(SetTrap(+ env, *result, "getOwnPropertyDescriptor", getOwnPropertyDescriptor_));+ CHECK_NAPI(SetTrap(env, *result, "getPrototypeOf", getPrototypeOf_));+ CHECK_NAPI(SetTrap(env, *result, "has", has_));+ CHECK_NAPI(SetTrap(env, *result, "isExtensible", isExtensible_));+ CHECK_NAPI(SetTrap(env, *result, "ownKeys", ownKeys_));+ CHECK_NAPI(SetTrap(env, *result, "preventExtensions", preventExtensions_));+ CHECK_NAPI(SetTrap(env, *result, "set", set_));+ CHECK_NAPI(SetTrap(env, *result, "setPrototypeOf", setPrototypeOf_));+ return napi_ok;+}++napi_value ProxyHandlerBuilder::Apply() noexcept {+ return apply_;+}++void ProxyHandlerBuilder::Apply(napi_value trap) noexcept {+ apply_ = trap;+}++napi_status ProxyHandlerBuilder::Apply(napi_env env,+ napi_callback trap,+ void* data) noexcept {+ return napi_create_function(+ env, "apply", NAPI_AUTO_LENGTH, trap, data, &apply_);+}++napi_status ProxyHandlerBuilder::Apply(+ napi_env env,+ napi_status (*trap)(napi_env env,+ napi_value target,+ napi_value thisArg,+ napi_value argumentList,+ napi_value* result)) {+ using TrapType = decltype(trap);+ static TrapCallInfo callInfo{[](napi_env env,+ void* trap,+ napi_span<napi_value> args,+ napi_value* result) noexcept {+ return reinterpret_cast<TrapType>(trap)(+ env, args[0], args[1], args[2], result);+ },+ reinterpret_cast<void*>(trap)};+ return Apply(env, CreateTrapCallback<3>(), &callInfo);+}++napi_status ProxyHandlerBuilder::Apply(+ napi_env env,+ napi_status (*trap)(napi_env env,+ napi_value target,+ napi_value thisArg,+ napi_span<napi_value> argumentList,+ napi_value* result)) {+ using TrapType = decltype(trap);+ static TrapCallInfo callInfo{+ [](napi_env env,+ void* trap,+ napi_span<napi_value> args,+ napi_value* result) noexcept {+ std::vector<napi_value> argVector;+ CHECK_NAPI(ToVector(env, args[2], &argVector));+ return reinterpret_cast<TrapType>(trap)(+ env,+ args[0],+ args[1],+ span<napi_value>(argVector.data(), argVector.size()),+ result);+ },+ reinterpret_cast<void*>(trap)};+ return Apply(env, CreateTrapCallback<3>(), &callInfo);+}++napi_value ProxyHandlerBuilder::Construct() noexcept {+ return construct_;+}++void ProxyHandlerBuilder::Construct(napi_value trap) noexcept {+ construct_ = trap;+}++napi_status ProxyHandlerBuilder::Construct(napi_env env,+ napi_callback trap,+ void* data) noexcept {+ return napi_create_function(+ env, "construct", NAPI_AUTO_LENGTH, trap, data, &construct_);+}++napi_status ProxyHandlerBuilder::Construct(+ napi_env env,+ napi_status (*trap)(napi_env env,+ napi_value target,+ napi_value argumentList,+ napi_value newTarget,+ napi_value* result)) {+ using TrapType = decltype(trap);+ static TrapCallInfo callInfo{[](napi_env env,+ void* trap,+ napi_span<napi_value> args,+ napi_value* result) noexcept {+ return reinterpret_cast<TrapType>(trap)(+ env, args[0], args[1], args[2], result);+ },+ reinterpret_cast<void*>(trap)};+ return Construct(env, CreateTrapCallback<3>(), &callInfo);+}++napi_status ProxyHandlerBuilder::Construct(+ napi_env env,+ napi_status (*trap)(napi_env env,+ napi_value target,+ napi_span<napi_value> argumentList,+ napi_value newTarget,+ napi_value* result)) {+ using TrapType = decltype(trap);+ static TrapCallInfo callInfo{+ [](napi_env env,+ void* trap,+ napi_span<napi_value> args,+ napi_value* result) noexcept {+ std::vector<napi_value> argVector;+ CHECK_NAPI(ToVector(env, args[1], &argVector));+ return reinterpret_cast<TrapType>(trap)(+ env,+ args[0],+ span<napi_value>(argVector.data(), argVector.size()),+ args[2],+ result);+ },+ reinterpret_cast<void*>(trap)};+ return Construct(env, CreateTrapCallback<3>(), &callInfo);+}++napi_value ProxyHandlerBuilder::DefineProperty() noexcept {+ return defineProperty_;+}++void ProxyHandlerBuilder::DefineProperty(napi_value trap) noexcept {+ defineProperty_ = trap;+}++napi_status ProxyHandlerBuilder::DefineProperty(napi_env env,+ napi_callback trap,+ void* data) noexcept {+ return napi_create_function(+ env, "defineProperty", NAPI_AUTO_LENGTH, trap, data, &defineProperty_);+}++napi_status ProxyHandlerBuilder::DefineProperty(+ napi_env env,+ napi_status (*trap)(napi_env env,+ napi_value target,+ napi_value key,+ napi_value descriptor,+ napi_value* result)) {+ using TrapType = decltype(trap);+ static TrapCallInfo callInfo{[](napi_env env,+ void* trap,+ napi_span<napi_value> args,+ napi_value* result) noexcept {+ return reinterpret_cast<TrapType>(trap)(+ env, args[0], args[1], args[2], result);+ },+ reinterpret_cast<void*>(trap)};+ return DefineProperty(env, CreateTrapCallback<3>(), &callInfo);+}++napi_status ProxyHandlerBuilder::DefineProperty(+ napi_env env,+ napi_status (*trap)(napi_env env,+ napi_value target,+ napi_value key,+ napi_value descriptor,+ bool* result)) {+ using TrapType = decltype(trap);+ static TrapCallInfo callInfo{+ [](napi_env env,+ void* trap,+ napi_span<napi_value> args,+ napi_value* result) noexcept {+ bool boolResult{};+ CHECK_NAPI(reinterpret_cast<TrapType>(trap)(+ env, args[0], args[1], args[2], &boolResult));+ return napi_get_boolean(env, boolResult, result);+ },+ reinterpret_cast<void*>(trap)};+ return DefineProperty(env, CreateTrapCallback<3>(), &callInfo);+}++napi_value ProxyHandlerBuilder::DeleteProperty() noexcept {+ return deleteProperty_;+}++void ProxyHandlerBuilder::DeleteProperty(napi_value trap) noexcept {+ deleteProperty_ = trap;+}++napi_status ProxyHandlerBuilder::DeleteProperty(napi_env env,+ napi_callback trap,+ void* data) noexcept {+ return napi_create_function(+ env, "deleteProperty", NAPI_AUTO_LENGTH, trap, data, &deleteProperty_);+}++napi_status ProxyHandlerBuilder::DeleteProperty(+ napi_env env,+ napi_status (*trap)(+ napi_env env, napi_value target, napi_value key, napi_value* result)) {+ using TrapType = decltype(trap);+ static TrapCallInfo callInfo{[](napi_env env,+ void* trap,+ napi_span<napi_value> args,+ napi_value* result) noexcept {+ return reinterpret_cast<TrapType>(trap)(+ env, args[0], args[1], result);+ },+ reinterpret_cast<void*>(trap)};+ return DeleteProperty(env, CreateTrapCallback<2>(), &callInfo);+}++napi_status ProxyHandlerBuilder::DeleteProperty(+ napi_env env,+ napi_status (*trap)(+ napi_env env, napi_value target, napi_value key, bool* result)) {+ using TrapType = decltype(trap);+ static TrapCallInfo callInfo{[](napi_env env,+ void* trap,+ napi_span<napi_value> args,+ napi_value* result) noexcept {+ bool boolResult{};+ CHECK_NAPI(reinterpret_cast<TrapType>(trap)(+ env, args[0], args[1], &boolResult));+ return napi_get_boolean(+ env, boolResult, result);+ },+ reinterpret_cast<void*>(trap)};+ return DeleteProperty(env, CreateTrapCallback<2>(), &callInfo);+}++napi_value ProxyHandlerBuilder::Get() noexcept {+ return get_;+}++void ProxyHandlerBuilder::Get(napi_value trap) noexcept {+ get_ = trap;+}++napi_status ProxyHandlerBuilder::Get(napi_env env,+ napi_callback trap,+ void* data) noexcept {+ return napi_create_function(env, "get", NAPI_AUTO_LENGTH, trap, data, &get_);+}++napi_status ProxyHandlerBuilder::Get(napi_env env,+ napi_status (*trap)(napi_env env,+ napi_value target,+ napi_value key,+ napi_value receiver,+ napi_value* result)) {+ using TrapType = decltype(trap);+ static TrapCallInfo callInfo{[](napi_env env,+ void* trap,+ napi_span<napi_value> args,+ napi_value* result) noexcept {+ return reinterpret_cast<TrapType>(trap)(+ env, args[0], args[1], args[2], result);+ },+ reinterpret_cast<void*>(trap)};+ return Get(env, CreateTrapCallback<3>(), &callInfo);+}++napi_value ProxyHandlerBuilder::GetOwnPropertyDescriptor() noexcept {+ return getOwnPropertyDescriptor_;+}++void ProxyHandlerBuilder::GetOwnPropertyDescriptor(napi_value trap) noexcept {+ getOwnPropertyDescriptor_ = trap;+}++napi_status ProxyHandlerBuilder::GetOwnPropertyDescriptor(napi_env env,+ napi_callback trap,+ void* data) noexcept {+ return napi_create_function(env,+ "getOwnPropertyDescriptor",+ NAPI_AUTO_LENGTH,+ trap,+ data,+ &getOwnPropertyDescriptor_);+}++napi_status ProxyHandlerBuilder::GetOwnPropertyDescriptor(+ napi_env env,+ napi_status (*trap)(+ napi_env env, napi_value target, napi_value key, napi_value* result)) {+ using TrapType = decltype(trap);+ static TrapCallInfo callInfo{[](napi_env env,+ void* trap,+ napi_span<napi_value> args,+ napi_value* result) noexcept {+ return reinterpret_cast<TrapType>(trap)(+ env, args[0], args[1], result);+ },+ reinterpret_cast<void*>(trap)};+ return GetOwnPropertyDescriptor(env, CreateTrapCallback<2>(), &callInfo);+}++napi_value ProxyHandlerBuilder::GetPrototypeOf() noexcept {+ return getPrototypeOf_;+}++void ProxyHandlerBuilder::GetPrototypeOf(napi_value trap) noexcept {+ getPrototypeOf_ = trap;+}++napi_status ProxyHandlerBuilder::GetPrototypeOf(napi_env env,+ napi_callback trap,+ void* data) noexcept {+ return napi_create_function(+ env, "getPrototypeOf", NAPI_AUTO_LENGTH, trap, data, &getPrototypeOf_);+}++napi_status ProxyHandlerBuilder::GetPrototypeOf(+ napi_env env,+ napi_status (*trap)(napi_env env, napi_value target, napi_value* result)) {+ using TrapType = decltype(trap);+ static TrapCallInfo callInfo{[](napi_env env,+ void* trap,+ napi_span<napi_value> args,+ napi_value* result) noexcept {+ return reinterpret_cast<TrapType>(trap)(+ env, args[0], result);+ },+ reinterpret_cast<void*>(trap)};+ return GetPrototypeOf(env, CreateTrapCallback<1>(), &callInfo);+}++napi_value ProxyHandlerBuilder::Has() noexcept {+ return has_;+}++void ProxyHandlerBuilder::Has(napi_value trap) noexcept {+ has_ = trap;+}++napi_status ProxyHandlerBuilder::Has(napi_env env,+ napi_callback trap,+ void* data) noexcept {+ return napi_create_function(env, "has", NAPI_AUTO_LENGTH, trap, data, &has_);+}++napi_status ProxyHandlerBuilder::Has(napi_env env,+ napi_status (*trap)(napi_env env,+ napi_value target,+ napi_value key,+ napi_value* result)) {+ using TrapType = decltype(trap);+ static TrapCallInfo callInfo{[](napi_env env,+ void* trap,+ napi_span<napi_value> args,+ napi_value* result) noexcept {+ return reinterpret_cast<TrapType>(trap)(+ env, args[0], args[1], result);+ },+ reinterpret_cast<void*>(trap)};+ return Has(env, CreateTrapCallback<2>(), &callInfo);+}++napi_status ProxyHandlerBuilder::Has(napi_env env,+ napi_status (*trap)(napi_env env,+ napi_value target,+ napi_value key,+ bool* result)) {+ using TrapType = decltype(trap);+ static TrapCallInfo callInfo{[](napi_env env,+ void* trap,+ napi_span<napi_value> args,+ napi_value* result) noexcept {+ bool boolResult{};+ CHECK_NAPI(reinterpret_cast<TrapType>(trap)(+ env, args[0], args[1], &boolResult));+ return napi_get_boolean(+ env, boolResult, result);+ },+ reinterpret_cast<void*>(trap)};+ return Has(env, CreateTrapCallback<2>(), &callInfo);+}++napi_value ProxyHandlerBuilder::IsExtensible() noexcept {+ return isExtensible_;+}++void ProxyHandlerBuilder::IsExtensible(napi_value trap) noexcept {+ isExtensible_ = trap;+}++napi_status ProxyHandlerBuilder::IsExtensible(napi_env env,+ napi_callback trap,+ void* data) noexcept {+ return napi_create_function(+ env, "isExtensible", NAPI_AUTO_LENGTH, trap, data, &isExtensible_);+}++napi_status ProxyHandlerBuilder::IsExtensible(+ napi_env env,+ napi_status (*trap)(napi_env env, napi_value target, napi_value* result)) {+ using TrapType = decltype(trap);+ static TrapCallInfo callInfo{[](napi_env env,+ void* trap,+ napi_span<napi_value> args,+ napi_value* result) noexcept {+ return reinterpret_cast<TrapType>(trap)(+ env, args[0], result);+ },+ reinterpret_cast<void*>(trap)};+ return IsExtensible(env, CreateTrapCallback<1>(), &callInfo);+}++napi_status ProxyHandlerBuilder::IsExtensible(+ napi_env env,+ napi_status (*trap)(napi_env env, napi_value target, bool* result)) {+ using TrapType = decltype(trap);+ static TrapCallInfo callInfo{+ [](napi_env env,+ void* trap,+ napi_span<napi_value> args,+ napi_value* result) noexcept {+ bool boolResult{};+ CHECK_NAPI(reinterpret_cast<TrapType>(trap)(env, args[0], &boolResult));+ return napi_get_boolean(env, boolResult, result);+ },+ reinterpret_cast<void*>(trap)};+ return IsExtensible(env, CreateTrapCallback<1>(), &callInfo);+}++napi_value ProxyHandlerBuilder::OwnKeys() noexcept {+ return ownKeys_;+}++void ProxyHandlerBuilder::OwnKeys(napi_value trap) noexcept {+ ownKeys_ = trap;+}++napi_status ProxyHandlerBuilder::OwnKeys(napi_env env,+ napi_callback trap,+ void* data) noexcept {+ return napi_create_function(+ env, "ownKeys", NAPI_AUTO_LENGTH, trap, data, &ownKeys_);+}++napi_status ProxyHandlerBuilder::OwnKeys(+ napi_env env,+ napi_status (*trap)(napi_env env, napi_value target, napi_value* result)) {+ using TrapType = decltype(trap);+ static TrapCallInfo callInfo{[](napi_env env,+ void* trap,+ napi_span<napi_value> args,+ napi_value* result) noexcept {+ return reinterpret_cast<TrapType>(trap)(+ env, args[0], result);+ },+ reinterpret_cast<void*>(trap)};+ return OwnKeys(env, CreateTrapCallback<1>(), &callInfo);+}++napi_value ProxyHandlerBuilder::PreventExtensions() noexcept {+ return preventExtensions_;+}++void ProxyHandlerBuilder::PreventExtensions(napi_value trap) noexcept {+ preventExtensions_ = trap;+}++napi_status ProxyHandlerBuilder::PreventExtensions(napi_env env,+ napi_callback trap,+ void* data) noexcept {+ return napi_create_function(env,+ "preventExtensions",+ NAPI_AUTO_LENGTH,+ trap,+ data,+ &preventExtensions_);+}++napi_status ProxyHandlerBuilder::PreventExtensions(+ napi_env env,+ napi_status (*trap)(napi_env env, napi_value target, napi_value* result)) {+ using TrapType = decltype(trap);+ static TrapCallInfo callInfo{[](napi_env env,+ void* trap,+ napi_span<napi_value> args,+ napi_value* result) noexcept {+ return reinterpret_cast<TrapType>(trap)(+ env, args[0], result);+ },+ reinterpret_cast<void*>(trap)};+ return PreventExtensions(env, CreateTrapCallback<1>(), &callInfo);+}++napi_status ProxyHandlerBuilder::PreventExtensions(+ napi_env env,+ napi_status (*trap)(napi_env env, napi_value target, bool* result)) {+ using TrapType = decltype(trap);+ static TrapCallInfo callInfo{+ [](napi_env env,+ void* trap,+ napi_span<napi_value> args,+ napi_value* result) noexcept {+ bool boolResult{};+ CHECK_NAPI(reinterpret_cast<TrapType>(trap)(env, args[0], &boolResult));+ return napi_get_boolean(env, boolResult, result);+ },+ reinterpret_cast<void*>(trap)};+ return PreventExtensions(env, CreateTrapCallback<1>(), &callInfo);+}++napi_value ProxyHandlerBuilder::Set() noexcept {+ return set_;+}++void ProxyHandlerBuilder::Set(napi_value trap) noexcept {+ set_ = trap;+}++napi_status ProxyHandlerBuilder::Set(napi_env env,+ napi_callback trap,+ void* data) noexcept {+ return napi_create_function(env, "set", NAPI_AUTO_LENGTH, trap, data, &set_);+}++napi_status ProxyHandlerBuilder::Set(napi_env env,+ napi_status (*trap)(napi_env env,+ napi_value target,+ napi_value key,+ napi_value value,+ napi_value receiver,+ napi_value* result)) {+ using TrapType = decltype(trap);+ static TrapCallInfo callInfo{+ [](napi_env env,+ void* trap,+ napi_span<napi_value> args,+ napi_value* result) noexcept {+ return reinterpret_cast<TrapType>(trap)(+ env, args[0], args[1], args[2], args[3], result);+ },+ reinterpret_cast<void*>(trap)};+ return Set(env, CreateTrapCallback<4>(), &callInfo);+}++napi_status ProxyHandlerBuilder::Set(napi_env env,+ napi_status (*trap)(napi_env env,+ napi_value target,+ napi_value key,+ napi_value value,+ napi_value receiver,+ bool* result)) {+ using TrapType = decltype(trap);+ static TrapCallInfo callInfo{+ [](napi_env env,+ void* trap,+ napi_span<napi_value> args,+ napi_value* result) noexcept {+ bool boolResult{};+ CHECK_NAPI(reinterpret_cast<TrapType>(trap)(+ env, args[0], args[1], args[2], args[3], &boolResult));+ return napi_get_boolean(env, boolResult, result);+ },+ reinterpret_cast<void*>(trap)};+ return Set(env, CreateTrapCallback<4>(), &callInfo);+}++napi_value ProxyHandlerBuilder::SetPrototypeOf() noexcept {+ return setPrototypeOf_;+}++void ProxyHandlerBuilder::SetPrototypeOf(napi_value trap) noexcept {+ setPrototypeOf_ = trap;+}++napi_status ProxyHandlerBuilder::SetPrototypeOf(napi_env env,+ napi_callback trap,+ void* data) noexcept {+ return napi_create_function(+ env, "setPrototypeOf", NAPI_AUTO_LENGTH, trap, data, &setPrototypeOf_);+}++napi_status ProxyHandlerBuilder::SetPrototypeOf(+ napi_env env,+ napi_status (*trap)(napi_env env,+ napi_value target,+ napi_value prototype,+ napi_value* result)) {+ using TrapType = decltype(trap);+ static TrapCallInfo callInfo{[](napi_env env,+ void* trap,+ napi_span<napi_value> args,+ napi_value* result) noexcept {+ return reinterpret_cast<TrapType>(trap)(+ env, args[0], args[1], result);+ },+ reinterpret_cast<void*>(trap)};+ return SetPrototypeOf(env, CreateTrapCallback<2>(), &callInfo);+}++napi_status ProxyHandlerBuilder::SetPrototypeOf(+ napi_env env,+ napi_status (*trap)(+ napi_env env, napi_value target, napi_value prototype, bool* result)) {+ using TrapType = decltype(trap);+ static TrapCallInfo callInfo{[](napi_env env,+ void* trap,+ napi_span<napi_value> args,+ napi_value* result) noexcept {+ bool boolResult{};+ CHECK_NAPI(reinterpret_cast<TrapType>(trap)(+ env, args[0], args[1], &boolResult));+ return napi_get_boolean(+ env, boolResult, result);+ },+ reinterpret_cast<void*>(trap)};+ return SetPrototypeOf(env, CreateTrapCallback<2>(), &callInfo);+}++//=============++ProxyTemplate::ProxyTemplate(napi_env env, napi_value proxyHandler) noexcept+ : proxyHandler_(RefHolder(env, proxyHandler)) {}++napi_status ProxyTemplate::NewInstance(napi_env env,+ napi_value target,+ napi_value* result) noexcept {+ napi_value proxyConstructor{};+ CHECK_NAPI(GetProxyConstructor(env, &proxyConstructor));++ napi_value args[] = {target, static_cast<napi_value>(proxyHandler_)};+ return napi_new_instance(env, proxyConstructor, 2, args, result);+}++napi_status ProxyTemplate::GetProxyConstructor(napi_env env,+ napi_value* result) noexcept {+ if (!proxyConstructor_) {+ napi_value global{}, proxyConstructor{};+ CHECK_NAPI(napi_get_global(env, &global));+ CHECK_NAPI(+ napi_get_named_property(env, global, "Proxy", &proxyConstructor));+ proxyConstructor_ = RefHolder(env, proxyConstructor);+ }+ *result = proxyConstructor_;+ return napi_ok;+}
src/2-js-to-native-conversion/object-template-demo/napi/proxy-template.h
@@ -0,0 +1,249 @@+#include "node-api-common.h"++template <typename T>+using napi_span = span<T>;++// Creates a handler object for a JavaScript Proxy.+// See+// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy+// for details about JavaScript Proxy.+//+// There are four different ways we can set each Proxy handler trap:+// - using napi_value function.+// - using napi_callback that creates function.+// - using a callback signature that matches Proxy handler definition arguments.+// - using a callback based on the Proxy handler trap signature where we replace+// napi_value with strongly typed values such as bool or+// napi_span<napi_value>.+//+// Note that ProxyHandlerBuilder keeps handler trap functions as napi_value+// and thus cannot be persisted. The typical usage is to create new+// ProxyHandlerBuilder instance on the call stack, set handler traps, create+// handler to pass to Proxy constructor or ProxyTemplate, and not to reuse the+// ProxyHandlerBuilder instance.+struct ProxyHandlerBuilder {+ napi_status NewHandler(napi_env env, napi_value* result) noexcept;++ napi_value Apply() noexcept;+ void Apply(napi_value trap) noexcept;+ napi_status Apply(napi_env env,+ napi_callback trap,+ void* data = nullptr) noexcept;+ napi_status Apply(napi_env env,+ napi_status (*trap)(napi_env env,+ napi_value target,+ napi_value thisArg,+ napi_value argumentList,+ napi_value* result));+ napi_status Apply(napi_env env,+ napi_status (*trap)(napi_env env,+ napi_value target,+ napi_value thisArg,+ napi_span<napi_value> argumentList,+ napi_value* result));++ napi_value Construct() noexcept;+ void Construct(napi_value trap) noexcept;+ napi_status Construct(napi_env env,+ napi_callback trap,+ void* data = nullptr) noexcept;+ napi_status Construct(napi_env env,+ napi_status (*trap)(napi_env env,+ napi_value target,+ napi_value argumentList,+ napi_value newTarget,+ napi_value* result));+ napi_status Construct(napi_env env,+ napi_status (*trap)(napi_env env,+ napi_value target,+ napi_span<napi_value> argumentList,+ napi_value newTarget,+ napi_value* result));++ napi_value DefineProperty() noexcept;+ void DefineProperty(napi_value trap) noexcept;+ napi_status DefineProperty(napi_env env,+ napi_callback trap,+ void* data = nullptr) noexcept;+ napi_status DefineProperty(napi_env env,+ napi_status (*trap)(napi_env env,+ napi_value target,+ napi_value key,+ napi_value descriptor,+ napi_value* result));+ napi_status DefineProperty(napi_env env,+ napi_status (*trap)(napi_env env,+ napi_value target,+ napi_value key,+ napi_value descriptor,+ bool* result));++ napi_value DeleteProperty() noexcept;+ void DeleteProperty(napi_value trap) noexcept;+ napi_status DeleteProperty(napi_env env,+ napi_callback trap,+ void* data = nullptr) noexcept;+ napi_status DeleteProperty(napi_env env,+ napi_status (*trap)(napi_env env,+ napi_value target,+ napi_value key,+ napi_value* result));+ napi_status DeleteProperty(napi_env env,+ napi_status (*trap)(napi_env env,+ napi_value target,+ napi_value key,+ bool* result));++ napi_value Get() noexcept;+ void Get(napi_value trap) noexcept;+ napi_status Get(napi_env env,+ napi_callback trap,+ void* data = nullptr) noexcept;+ napi_status Get(napi_env env,+ napi_status (*trap)(napi_env env,+ napi_value target,+ napi_value key,+ napi_value receiver,+ napi_value* result));++ napi_value GetOwnPropertyDescriptor() noexcept;+ void GetOwnPropertyDescriptor(napi_value trap) noexcept;+ napi_status GetOwnPropertyDescriptor(napi_env env,+ napi_callback trap,+ void* data = nullptr) noexcept;+ napi_status GetOwnPropertyDescriptor(napi_env env,+ napi_status (*trap)(napi_env env,+ napi_value target,+ napi_value key,+ napi_value* result));++ napi_value GetPrototypeOf() noexcept;+ void GetPrototypeOf(napi_value trap) noexcept;+ napi_status GetPrototypeOf(napi_env env,+ napi_callback trap,+ void* data = nullptr) noexcept;+ napi_status GetPrototypeOf(napi_env env,+ napi_status (*trap)(napi_env env,+ napi_value target,+ napi_value* result));++ napi_value Has() noexcept;+ void Has(napi_value trap) noexcept;+ napi_status Has(napi_env env,+ napi_callback trap,+ void* data = nullptr) noexcept;+ napi_status Has(napi_env env,+ napi_status (*trap)(napi_env env,+ napi_value target,+ napi_value key,+ napi_value* result));+ napi_status Has(napi_env env,+ napi_status (*trap)(napi_env env,+ napi_value target,+ napi_value key,+ bool* result));++ napi_value IsExtensible() noexcept;+ void IsExtensible(napi_value trap) noexcept;+ napi_status IsExtensible(napi_env env,+ napi_callback trap,+ void* data = nullptr) noexcept;+ napi_status IsExtensible(napi_env env,+ napi_status (*trap)(napi_env env,+ napi_value target,+ napi_value* result));+ napi_status IsExtensible(napi_env env,+ napi_status (*trap)(napi_env env,+ napi_value target,+ bool* result));++ napi_value OwnKeys() noexcept;+ void OwnKeys(napi_value trap) noexcept;+ napi_status OwnKeys(napi_env env,+ napi_callback trap,+ void* data = nullptr) noexcept;+ napi_status OwnKeys(napi_env env,+ napi_status (*trap)(napi_env env,+ napi_value target,+ napi_value* result));++ napi_value PreventExtensions() noexcept;+ void PreventExtensions(napi_value trap) noexcept;+ napi_status PreventExtensions(napi_env env,+ napi_callback trap,+ void* data = nullptr) noexcept;+ napi_status PreventExtensions(napi_env env,+ napi_status (*trap)(napi_env env,+ napi_value target,+ napi_value* result));+ napi_status PreventExtensions(napi_env env,+ napi_status (*trap)(napi_env env,+ napi_value target,+ bool* result));++ napi_value Set() noexcept;+ void Set(napi_value trap) noexcept;+ napi_status Set(napi_env env,+ napi_callback trap,+ void* data = nullptr) noexcept;+ napi_status Set(napi_env env,+ napi_status (*trap)(napi_env env,+ napi_value target,+ napi_value key,+ napi_value value,+ napi_value receiver,+ napi_value* result));+ napi_status Set(napi_env env,+ napi_status (*trap)(napi_env env,+ napi_value target,+ napi_value key,+ napi_value value,+ napi_value receiver,+ bool* result));++ napi_value SetPrototypeOf() noexcept;+ void SetPrototypeOf(napi_value trap) noexcept;+ napi_status SetPrototypeOf(napi_env env,+ napi_callback trap,+ void* data = nullptr) noexcept;+ napi_status SetPrototypeOf(napi_env env,+ napi_status (*trap)(napi_env env,+ napi_value target,+ napi_value prototype,+ napi_value* result));+ napi_status SetPrototypeOf(napi_env env,+ napi_status (*trap)(napi_env env,+ napi_value target,+ napi_value prototype,+ bool* result));++ private:+ napi_value apply_{};+ napi_value construct_{};+ napi_value defineProperty_{};+ napi_value deleteProperty_{};+ napi_value get_{};+ napi_value getOwnPropertyDescriptor_{};+ napi_value getPrototypeOf_{};+ napi_value has_{};+ napi_value isExtensible_{};+ napi_value ownKeys_{};+ napi_value preventExtensions_{};+ napi_value set_{};+ napi_value setPrototypeOf_{};+};++struct ProxyTemplate {+ ProxyTemplate(napi_env env, napi_value proxyHandler) noexcept;++ napi_status NewInstance(napi_env env,+ napi_value target,+ napi_value* result) noexcept;++ private:+ napi_status GetProxyConstructor(napi_env env, napi_value* result) noexcept;++ private:+ RefHolder proxyConstructor_{};+ RefHolder proxyHandler_{};+};
src/2-js-to-native-conversion/object-wrap-demo/node-addon-api/README.md
@@ -0,0 +1,14 @@+# Node-API Object Wrap Demo++This is an example project that accompanies the Node-API workshop tutorials++A tutorial describing this project can be found at the [Node-API Resource](https://napi.inspiredware.com/getting-started/objectwrap.html).++To build and run this program on your system, clone it to your computer and run these two commands inside your clone:++```+npm install+npm test+```++> You need to have Node 10.5.0 or later installed.
src/2-js-to-native-conversion/object-wrap-demo/node-addon-api/binding.gyp
@@ -0,0 +1,20 @@+{+ 'targets': [+ {+ 'target_name': 'object-wrap-demo-native',+ 'sources': [ 'src/object_wrap_demo.cc' ],+ 'include_dirs': ["<!@(node -p \"require('node-addon-api').include\")"],+ 'dependencies': ["<!(node -p \"require('node-addon-api').gyp\")"],+ 'cflags!': [ '-fno-exceptions' ],+ 'cflags_cc!': [ '-fno-exceptions' ],+ 'xcode_settings': {+ 'GCC_ENABLE_CPP_EXCEPTIONS': 'YES',+ 'CLANG_CXX_LIBRARY': 'libc++',+ 'MACOSX_DEPLOYMENT_TARGET': '10.7'+ },+ 'msvs_settings': {+ 'VCCLCompilerTool': { 'ExceptionHandling': 1 },+ }+ }+ ]+}
src/2-js-to-native-conversion/object-wrap-demo/node-addon-api/lib/binding.js
@@ -0,0 +1,11 @@+const addon = require('../build/Release/object-wrap-demo-native');++function ObjectWrapDemo(name) {+ this.greet = function(str) {+ return _addonInstance.greet(str);+ }++ var _addonInstance = new addon.ObjectWrapDemo(name);+}++module.exports = ObjectWrapDemo;
src/2-js-to-native-conversion/object-wrap-demo/node-addon-api/package.json
@@ -0,0 +1,16 @@+{+ "main": "lib/binding.js",+ "private": true,+ "dependencies": {+ "node-addon-api": "^1.1.0"+ },+ "scripts": {+ "test": "node --napi-modules ./test/test_binding.js"+ },+ "gypfile": true,+ "name": "object-wrap-demo",+ "version": "1.0.0",+ "description": "An object wrapper demo",+ "author": "Your name goes here",+ "license": "MIT"+}
src/2-js-to-native-conversion/object-wrap-demo/node-addon-api/src/object_wrap_demo.cc
@@ -0,0 +1,62 @@+#include "object_wrap_demo.h"++using namespace Napi;++ObjectWrapDemo::ObjectWrapDemo(const Napi::CallbackInfo& info)+ : ObjectWrap(info) {+ Napi::Env env = info.Env();++ if (info.Length() < 1) {+ Napi::TypeError::New(env, "Wrong number of arguments")+ .ThrowAsJavaScriptException();+ return;+ }++ if (!info[0].IsString()) {+ Napi::TypeError::New(env, "You need to name yourself")+ .ThrowAsJavaScriptException();+ return;+ }++ this->_greeterName = info[0].As<Napi::String>().Utf8Value();+}++Napi::Value ObjectWrapDemo::Greet(const Napi::CallbackInfo& info) {+ Napi::Env env = info.Env();++ if (info.Length() < 1) {+ Napi::TypeError::New(env, "Wrong number of arguments")+ .ThrowAsJavaScriptException();+ return env.Null();+ }++ if (!info[0].IsString()) {+ Napi::TypeError::New(env, "You need to introduce yourself to greet")+ .ThrowAsJavaScriptException();+ return env.Null();+ }++ Napi::String name = info[0].As<Napi::String>();++ printf("Hello %s\n", name.Utf8Value().c_str());+ printf("I am %s\n", this->_greeterName.c_str());++ return Napi::String::New(env, this->_greeterName);+}++Napi::Function ObjectWrapDemo::GetClass(Napi::Env env) {+ return DefineClass(+ env,+ "ObjectWrapDemo",+ {+ ObjectWrapDemo::InstanceMethod("greet", &ObjectWrapDemo::Greet),+ });+}++Napi::Object Init(Napi::Env env, Napi::Object exports) {+ Napi::String name = Napi::String::New(env, "ObjectWrapDemo");+ exports.Set(name, ObjectWrapDemo::GetClass(env));+ return exports;+}++NODE_API_MODULE(addon, Init)
src/2-js-to-native-conversion/object-wrap-demo/node-addon-api/src/object_wrap_demo.h
@@ -0,0 +1,14 @@+#pragma once++#include <napi.h>++class ObjectWrapDemo : public Napi::ObjectWrap<ObjectWrapDemo> {+ public:+ ObjectWrapDemo(const Napi::CallbackInfo&);+ Napi::Value Greet(const Napi::CallbackInfo&);++ static Napi::Function GetClass(Napi::Env);++ private:+ std::string _greeterName;+};
src/2-js-to-native-conversion/object-wrap-demo/node-addon-api/test/test_binding.js
@@ -0,0 +1,21 @@+const ObjectWrapDemo = require("../lib/binding.js");+const assert = require("assert");++assert(ObjectWrapDemo, "The expected module is undefined");++function testBasic()+{+ const instance = new ObjectWrapDemo("mr-yeoman");+ assert(instance.greet, "The expected method is not defined");+ assert.strictEqual(instance.greet("kermit"), "mr-yeoman", "Unexpected value returned");+}++function testInvalidParams()+{+ const instance = new ObjectWrapDemo();+}++assert.doesNotThrow(testBasic, undefined, "testBasic threw an expection");+assert.throws(testInvalidParams, undefined, "testInvalidParams didn't throw");++console.log("Tests passed- everything looks OK!");
src/2-js-to-native-conversion/typed_array_to_native/node-addon-api/binding.gyp
@@ -0,0 +1,14 @@+{+ "targets": [+ {+ "target_name": "typed_array_to_native",+ "cflags!": [ "-fno-exceptions" ],+ "cflags_cc!": [ "-fno-exceptions" ],+ "sources": [ "typed_array_to_native.cc" ],+ "include_dirs": [+ "<!@(node -p \"require('node-addon-api').include\")"+ ],+ 'defines': [ 'NAPI_DISABLE_CPP_EXCEPTIONS' ],+ }+ ]+}
src/2-js-to-native-conversion/typed_array_to_native/node-addon-api/index.js
@@ -0,0 +1,15 @@+const binding = require('bindings')('typed_array_to_native');++{+ const byteArray = Uint8Array.from([10, 20, 30, 50, 25, 17]);+ console.log("byteArray: ", byteArray);+ binding.AcceptByteArray(byteArray);+}+console.log("---------");++{+ const array = [7, 15, 26, 58, 64];+ console.log("array: ", array);+ const byteArray = binding.CreateByteArray(array);+ console.log("byteArray: ", byteArray);+}
src/2-js-to-native-conversion/typed_array_to_native/node-addon-api/package.json
@@ -0,0 +1,14 @@+{+ "name": "typed_array_to_native",+ "version": "0.0.0",+ "description": "Shows how to read and create typed arrays",+ "main": "index.js",+ "private": true,+ "dependencies": {+ "bindings": "~1.2.1",+ "node-addon-api": "^1.0.0"+ },+ "scripts": {+ "test": "node index.js"+ }+}
src/2-js-to-native-conversion/typed_array_to_native/node-addon-api/typed_array_to_native.cc
@@ -0,0 +1,109 @@+#include <napi.h>+#include <cstdio>++static Napi::Value AcceptByteArray(const Napi::CallbackInfo& info) {+ if (info.Length() != 1) {+ Napi::Error::New(info.Env(), "Expected exactly one argument")+ .ThrowAsJavaScriptException();+ return info.Env().Undefined();+ }++ if (!info[0].IsTypedArray()) {+ Napi::Error::New(info.Env(), "Expected a TypedArray")+ .ThrowAsJavaScriptException();+ return info.Env().Undefined();+ }+ Napi::TypedArray typedArray = info[0].As<Napi::TypedArray>();++ if (typedArray.TypedArrayType() != napi_uint8_array) {+ Napi::Error::New(info.Env(), "Expected an Uint8Array")+ .ThrowAsJavaScriptException();+ return info.Env().Undefined();+ }+ Napi::Uint8Array uint8Array = typedArray.As<Napi::Uint8Array>();++ // Copy to std::vector<uint8_t>:+ std::vector<uint8_t> bytes(uint8Array.Data(),+ uint8Array.Data() + uint8Array.ElementLength());+ printf("std::vector<uint8_t> from Uint8Array: [");+ for (uint8_t byte : bytes) {+ printf("%d, ", byte);+ }+ printf("\b\b]\n");++ return info.Env().Undefined();+}++static Napi::Value CreateByteArray(const Napi::CallbackInfo& info) {+ if (info.Length() != 1) {+ Napi::Error::New(info.Env(), "Expected exactly one argument")+ .ThrowAsJavaScriptException();+ return info.Env().Undefined();+ }++ if (!info[0].IsArray()) {+ Napi::Error::New(info.Env(), "Expected an Array")+ .ThrowAsJavaScriptException();+ return info.Env().Undefined();+ }+ Napi::Array array = info[0].As<Napi::Array>();+ size_t arrayLength = array.Length();++ // Create std::vector<uint8_t> out of array.+ // We allocate it on the heap to allow wrapping it up into ArrayBuffer.+ std::unique_ptr<std::vector<uint8_t>> nativeArray =+ std::make_unique<std::vector<uint8_t>>(arrayLength, 0);+ for (size_t i = 0; i < arrayLength; ++i) {+ Napi::Value arrayItem = array[i];+ if (!arrayItem.IsNumber()) {+ Napi::Error::New(info.Env(), "Expected a Number as Array Item")+ .ThrowAsJavaScriptException();+ return info.Env().Undefined();+ }+ Napi::Number number = arrayItem.As<Napi::Number>();+ double numberValue = number.DoubleValue();+ if (numberValue < 0 || numberValue > 255) {+ Napi::Error::New(info.Env(),+ "Array Item Number value is out of range [0..255]")+ .ThrowAsJavaScriptException();+ return info.Env().Undefined();+ }++ (*nativeArray)[i] = static_cast<uint8_t>(numberValue);+ }++ printf("std::vector<uint8_t> from Array: [");+ for (uint8_t byte : *nativeArray) {+ printf("%d, ", byte);+ }+ printf("\b\b]\n");++ // Wrap up the std::vector into the ArrayBuffer.+ // Note: instead of wrapping the std::vector we could allow ArrayBuffer to+ // create internal storage that copies the std::vector, but it is less+ // efficient because it requires an extra memory allocation.+ Napi::ArrayBuffer arrayBuffer = Napi::ArrayBuffer::New(+ info.Env(),+ nativeArray->data(),+ arrayLength /* size in bytes */,+ [](Napi::Env /*env*/, void* /*data*/, std::vector<uint8_t>* hint) {+ std::unique_ptr<std::vector<uint8_t>> vectorPtrToDelete(hint);+ },+ nativeArray.get());+ // The finalizer is responsible for deleting the vector: release the+ // unique_ptr ownership.+ nativeArray.release();++ Napi::Uint8Array byteArray =+ Napi::Uint8Array::New(info.Env(), arrayLength, arrayBuffer, 0);++ return byteArray;+}++static Napi::Object Init(Napi::Env env, Napi::Object exports) {+ exports["AcceptByteArray"] = Napi::Function::New(env, AcceptByteArray);+ exports["CreateByteArray"] = Napi::Function::New(env, CreateByteArray);+ return exports;+}++NODE_API_MODULE(NODE_GYP_MODULE_NAME, Init)
src/3-context-awareness/napi/binding.gyp
@@ -0,0 +1,8 @@+{+ 'targets': [+ {+ 'target_name': 'multiple_load',+ 'sources': [ 'multiple_load.c' ]+ }+ ]+}
src/3-context-awareness/napi/index.js
@@ -0,0 +1,40 @@+// Example illustrating the case where a native addon is loaded multiple times.+// This entire file is executed twice, concurrently - once on the main thread,+// and once on a thread launched from the main thread.++// We load the worker threads module, which allows us to launch multiple Node.js+// environments, each in its own thread.+const {+ Worker, isMainThread+} = require('worker_threads');++// We load the native addon.+const addon = require('bindings')('multiple_load');++// The iteration count can be tweaked to ensure that the output from the two+// threads is interleaved. Too few iterations and the output of one thread+// follows the output of the other, not really illustrating the concurrency.+const iterations = 1000;++// This function is an idle loop that performs a random walk from 0 by calling+// into the native addon to either increment or decrement the initial value.+function useAddon(addon, prefix, iterations) {+ if (iterations >= 0) {+ if (Math.random() < 0.5) {+ console.log(prefix + ': new value (decremented): ' + addon.decrement());+ } else {+ console.log(prefix + ': new value (incremented): ' + addon.increment());+ }+ setImmediate(() => useAddon(addon, prefix, --iterations));+ }+}++if (isMainThread) {+ // On the main thread, we launch a worker and wait for it to come online. Then+ // we start the loop.+ (new Worker(__filename)).on('online',+ () => useAddon(addon, "Main thread", iterations));+} else {+ // On the secondary thread we immediately start the loop.+ useAddon(addon, "Worker thread", iterations);+}
src/3-context-awareness/napi/multiple_load.c
@@ -0,0 +1,145 @@+#include <stdio.h>+#include <math.h>+#include <stdlib.h>+#include <node_api.h>++#define CHECK(expr) \+ { \+ if ((expr) == 0) { \+ fprintf(stderr, "%s:%d: failed assertion `%s'\n", __FILE__, __LINE__, #expr); \+ fflush(stderr); \+ abort(); \+ } \+ }++// Structure containing information needed for as long as the addon exists. It+// replaces the use of global static data with per-addon-instance data by+// associating an instance of this structure with each instance of this addon+// during addon initialization. The instance of this structure is then passed to+// each binding the addon provides. Thus, the data stored in an instance of this+// structure is available to each binding, just as global static data would be.+typedef struct {+ double value;+} AddonData;++// This is the actual, useful work performed: increment or decrement the value+// stored per addon instance after passing it through a CPU-consuming but+// otherwise useless calculation.+static int ModifyAddonData(AddonData* data, double offset) {+ // Expensively increment or decrement the value.+ data->value = tan(atan(exp(log(sqrt(data->value * data->value))))) + offset;++ // Round the value to the nearest integer.+ data->value =+ (double)(((int)data->value) ++ (data->value - ((double)(int)data->value) > 0.5 ? 1 : 0));++ // Return the value as an integer.+ return (int)(data->value);+}++// This is boilerplate. The instance of the `AddonData` structure created during+// addon initialization must be destroyed when the addon is unloaded. This+// function will be called when the addon's `exports` object is garbage collected.+static void DeleteAddonData(napi_env env, void* data, void* hint) {+ // Avoid unused parameter warnings.+ (void) env;+ (void) hint;++ // Free the per-addon-instance data.+ free(data);+}++// This is also boilerplate. It creates and initializes an instance of the+// `AddonData` structure and ties its lifecycle to that of the addon instance's+// `exports` object. This means that the data will be available to this instance+// of the addon for as long as the JavaScript engine keeps it alive.+static AddonData* CreateAddonData(napi_env env, napi_value exports) {+ AddonData* result = malloc(sizeof(*result));+ result->value = 0.0;+ CHECK(napi_wrap(env,+ exports,+ result,+ DeleteAddonData,+ NULL,+ NULL) == napi_ok);+ return result;+}++// This function is called from JavaScript. It uses an expensive operation to+// increment the value stored inside the `AddonData` structure by one.+static napi_value Increment(napi_env env, napi_callback_info info) {+ // Retrieve the per-addon-instance data.+ AddonData* addon_data = NULL;+ CHECK(napi_get_cb_info(env,+ info,+ NULL,+ NULL,+ NULL,+ ((void**)&addon_data)) == napi_ok);++ // Increment the per-addon-instance value and create a new JavaScript integer+ // from it.+ napi_value result;+ CHECK(napi_create_int32(env,+ ModifyAddonData(addon_data, 1.0),+ &result) == napi_ok);++ // Return the JavaScript integer back to JavaScript.+ return result;+}++// This function is called from JavaScript. It uses an expensive operation to+// decrement the value stored inside the `AddonData` structure by one.+static napi_value Decrement(napi_env env, napi_callback_info info) {+ // Retrieve the per-addon-instance data.+ AddonData* addon_data = NULL;+ CHECK(napi_get_cb_info(env,+ info,+ NULL,+ NULL,+ NULL,+ ((void**)&addon_data)) == napi_ok);++ // Decrement the per-addon-instance value and create a new JavaScript integer+ // from it.+ napi_value result;+ CHECK(napi_create_int32(env,+ ModifyAddonData(addon_data, -1.0),+ &result) == napi_ok);++ // Return the JavaScript integer back to JavaScript.+ return result;+}++// Initialize the addon in such a way that it may be initialized multiple times+// per process. The function body following this macro is provided the value+// `env` which has type `napi_env` and the value `exports` which has type+// `napi_value` and which refers to a JavaScript object that ultimately contains+// the functions this addon wishes to expose. At the end, it must return a+// `napi_value`. It may return `exports`, or it may create a new `napi_value`+// and return that instead.+NAPI_MODULE_INIT(/*env, exports*/) {+ // Create a new instance of the per-instance-data that will be associated with+ // the instance of the addon being initialized here and that will be destroyed+ // along with the instance of the addon.+ AddonData* addon_data = CreateAddonData(env, exports);++ // Declare the bindings this addon provides. The data created above is given+ // as the last initializer parameter, and will be given to the binding when it+ // is called.+ napi_property_descriptor bindings[] = {+ {"increment", NULL, Increment, NULL, NULL, NULL, napi_enumerable, addon_data},+ {"decrement", NULL, Decrement, NULL, NULL, NULL, napi_enumerable, addon_data}+ };++ // Expose the two bindings declared above to JavaScript.+ CHECK(napi_define_properties(env,+ exports,+ sizeof(bindings) / sizeof(bindings[0]),+ bindings) == napi_ok);++ // Return the `exports` object provided. It now has two new properties, which+ // are the functions we wish to expose to JavaScript.+ return exports;+}
src/3-context-awareness/napi/package.json
@@ -0,0 +1,17 @@+{+ "name": "multiple_load",+ "version": "0.0.0",+ "description": "Multiple load example",+ "main": "index.js",+ "private": true,+ "scripts": {+ "test": "node --experimental-worker index.js"+ },+ "engines": {+ "node": ">= 10.10.0"+ },+ "gypfile": true,+ "dependencies": {+ "bindings": "~1.2.1"+ }+}
src/3-context-awareness/node_10/binding.gyp
@@ -0,0 +1,8 @@+{+ 'targets': [+ {+ 'target_name': 'multiple_load',+ 'sources': [ 'multiple_load.cc' ]+ }+ ]+}
src/3-context-awareness/node_10/index.js
@@ -0,0 +1,40 @@+// Example illustrating the case where a native addon is loaded multiple times.+// This entire file is executed twice, concurrently - once on the main thread,+// and once on a thread launched from the main thread.++// We load the worker threads module, which allows us to launch multiple Node.js+// environments, each in its own thread.+const {+ Worker, isMainThread+} = require('worker_threads');++// We load the native addon.+const addon = require('bindings')('multiple_load');++// The iteration count can be tweaked to ensure that the output from the two+// threads is interleaved. Too few iterations and the output of one thread+// follows the output of the other, not really illustrating the concurrency.+const iterations = 1000;++// This function is an idle loop that performs a random walk from 0 by calling+// into the native addon to either increment or decrement the initial value.+function useAddon(addon, prefix, iterations) {+ if (iterations >= 0) {+ if (Math.random() < 0.5) {+ console.log(prefix + ': new value (decremented): ' + addon.decrement());+ } else {+ console.log(prefix + ': new value (incremented): ' + addon.increment());+ }+ setImmediate(() => useAddon(addon, prefix, --iterations));+ }+}++if (isMainThread) {+ // On the main thread, we launch a worker and wait for it to come online. Then+ // we start the loop.+ (new Worker(__filename)).on('online',+ () => useAddon(addon, "Main thread", iterations));+} else {+ // On the secondary thread we immediately start the loop.+ useAddon(addon, "Worker thread", iterations);+}
src/3-context-awareness/node_10/multiple_load.cc
@@ -0,0 +1,121 @@+#include <math.h>+#include <node.h>++using namespace v8;++// Class containing information needed for as long as the addon exists. It+// replaces the use of global static data with per-addon-instance data by+// associating an instance of this class with each instance of this addon during+// addon initialization. The instance of this class is then passed to each+// binding the addon provides. Thus, the data stored in an instance of this+// class is available to each binding, just as global static data would be.+class AddonData {+ public:+ // This is the actual, useful work performed: increment or decrement the value+ // stored per addon instance after passing it through a CPU-consuming but+ // otherwise useless calculation. Round the result to the nearest integer.+ int GetNewValue(double offset) {+ // Expensively increment or decrement the value.+ value = tan(atan(exp(log(sqrt(value * value))))) + offset;++ // Round the value to the nearest integer.+ value =+ (double)(((int)value) + (value - ((double)(int)value) > 0.5 ? 1 : 0));++ // Return the value as an integer.+ return (int)value;+ }++ // This is boilerplate. It creates a new instance of this class and wraps it+ // into a v8::External. The isolate and the exports are necessary, because we+ // want the instance of this class to be destroyed along with the exports+ // object when the addon is eventually unloaded.+ static Local<Value> New(Isolate* isolate, Local<Object> exports) {+ return External::New(isolate, new AddonData(isolate, exports));+ }++ private:+ // This is the actual, useful payload carried by an instance of this class.+ // A double value is kept around for as long as an instance of this addon is+ // loaded, and is incremented or decremented whenever the addon receives a+ // call from JavaScript.+ double value;++ explicit AddonData(Isolate* isolate, Local<Object> exports)+ : // The payload is initialized here. The rest of the constructor is+ // boilerplate that ensures that the instance of this addon data is+ // destroyed along with the instance of this addon (`exports`).+ value(0.0) {+ exports_persistent.Reset(isolate, exports);+ exports_persistent.SetWeak(this, DeleteMe, WeakCallbackType::kParameter);+ }++ // The rest of the class definition is boilerplate.++ // The persistent reference must be reset before the instance of this class is+ // destroyed, otherwise memory will leak on the V8 side.+ ~AddonData() { exports_persistent.Reset(); }++ // This static function will be called when the addon instance is unloaded. It+ // merely destroys the per-addon-instance data.+ static void DeleteMe(const WeakCallbackInfo<AddonData>& info) {+ delete info.GetParameter();+ }++ Persistent<Object> exports_persistent;+};++// Function called from JavaScript to increment the value stored in this addon.+void Increment(const FunctionCallbackInfo<Value>& info) {+ // Retrieve the per-addon-instance data.+ AddonData* addon_data =+ static_cast<AddonData*>(info.Data().As<External>()->Value());++ info.GetReturnValue().Set(+ Number::New(info.GetIsolate(), addon_data->GetNewValue(1.0)));+}++// Function called from JavaScript to decrement the value stored in this addon.+void Decrement(const FunctionCallbackInfo<Value>& info) {+ // Retrieve the per-addon-instance data.+ AddonData* addon_data =+ static_cast<AddonData*>(info.Data().As<External>()->Value());++ info.GetReturnValue().Set(+ Number::New(info.GetIsolate(), addon_data->GetNewValue(-1.0)));+}++// Initialize the addon in such a way that it may be initialized multiple times+// per process. The function body following this macro is provided the value+// `exports` of type `Local<Object>`, the value `module` of type+// `Local<Object>`, and `context` of type `Local<Context>`. It may either define+// new properties on the `exports` object, or define the property named+// "exports" on the `module` object.+NODE_MODULE_INIT(/*exports, module, context*/) {+ Isolate* isolate = context->GetIsolate();++ // Create a new instance of the addon data that will be associated with this+ // instance of the addon, and that will be freed along with this instance of+ // the addon.+ Local<Value> addon_data = AddonData::New(isolate, exports);++ // Export the functions we wish to make available to JavaScript.++ exports+ ->Set(context,+ String::NewFromUtf8(isolate, "increment", NewStringType::kNormal)+ .ToLocalChecked(),+ FunctionTemplate::New(isolate, Increment, addon_data)+ ->GetFunction(context)+ .ToLocalChecked())+ .FromJust();++ exports+ ->Set(context,+ String::NewFromUtf8(isolate, "decrement", NewStringType::kNormal)+ .ToLocalChecked(),+ FunctionTemplate::New(isolate, Decrement, addon_data)+ ->GetFunction(context)+ .ToLocalChecked())+ .FromJust();+}
src/3-context-awareness/node_10/package.json
@@ -0,0 +1,17 @@+{+ "name": "multiple_load",+ "version": "0.0.0",+ "description": "Multiple load example",+ "main": "index.js",+ "private": true,+ "scripts": {+ "test": "node --experimental-worker index.js"+ },+ "engines": {+ "node": ">= 10.10.0"+ },+ "gypfile": true,+ "dependencies": {+ "bindings": "~1.2.1"+ }+}
src/4-references-and-handle-scope/function-reference-demo/node-addon-api/binding.gyp
@@ -0,0 +1,31 @@+{+ "targets": [+ {+ "target_name": "addon",+ "sources": [+ "src/binding.cc",+ "src/native-addon.cc"+ ],+ 'cflags!': [ '-fno-exceptions' ],+ 'cflags_cc!': [ '-fno-exceptions' ],+ 'include_dirs': ["<!@(node -p \"require('node-addon-api').include\")"],+ 'dependencies': ["<!(node -p \"require('node-addon-api').gyp\")"],+ 'conditions': [+ ['OS=="win"', {+ "msvs_settings": {+ "VCCLCompilerTool": {+ "ExceptionHandling": 1+ }+ }+ }],+ ['OS=="mac"', {+ "xcode_settings": {+ "CLANG_CXX_LIBRARY": "libc++",+ 'GCC_ENABLE_CPP_EXCEPTIONS': 'YES',+ 'MACOSX_DEPLOYMENT_TARGET': '10.7'+ }+ }]+ ]+ }+ ]+}
src/4-references-and-handle-scope/function-reference-demo/node-addon-api/index.js
@@ -0,0 +1,27 @@+'use strict'++const { NativeAddon } = require('bindings')('addon')++function JSFnRef() {+ console.log('Hi! I\'m a JS function reference!')+}++function JSFn() {+ console.log('Hi! I\'m a JS function!')+}++const ITERATIONS = 5++const addon = new NativeAddon(JSFnRef, JSFn)++for (let i = 0; i < ITERATIONS; i++) {+ addon.tryCallByStoredReference()+}++try {+ addon.tryCallByStoredFunction()+} catch (err) {+ console.error('This code crashes because JSFn is valid only inside the constructor function.')+ console.error('The lifespan of the JSFn function is limited to the execution of the constructor function.')+ console.error('After that, the function stored in JSFn is ready to be garbage collected and it cannot be used anymore.')+}
src/4-references-and-handle-scope/function-reference-demo/node-addon-api/package.json
@@ -0,0 +1,14 @@+{+ "name": "function-reference-demo",+ "version": "0.0.0",+ "description": "Tutorial example showing the use of function references.",+ "main": "index.js",+ "gypfile": true,+ "scripts": {+ "start": "node index.js"+ },+ "dependencies": {+ "node-addon-api": "*",+ "bindings": "*"+ }+}
src/4-references-and-handle-scope/function-reference-demo/node-addon-api/src/binding.cc
@@ -0,0 +1,9 @@+#include <napi.h>+#include "native-addon.h"++Napi::Object Init(Napi::Env env, Napi::Object exports) {+ NativeAddon::Init(env, exports);+ return exports;+}++NODE_API_MODULE(NODE_GYP_MODULE_NAME, Init)
src/4-references-and-handle-scope/function-reference-demo/node-addon-api/src/native-addon.cc
@@ -0,0 +1,36 @@+#include "native-addon.h"+#include <iostream>++Napi::FunctionReference NativeAddon::constructor;++Napi::Object NativeAddon::Init(Napi::Env env, Napi::Object exports) {+ Napi::Function func =+ DefineClass(env,+ "NativeAddon",+ {InstanceMethod("tryCallByStoredReference",+ &NativeAddon::TryCallByStoredReference),+ InstanceMethod("tryCallByStoredFunction",+ &NativeAddon::TryCallByStoredFunction)});++ constructor = Napi::Persistent(func);+ constructor.SuppressDestruct();++ exports.Set("NativeAddon", func);+ return exports;+}++NativeAddon::NativeAddon(const Napi::CallbackInfo& info)+ : Napi::ObjectWrap<NativeAddon>(info) {+ jsFnRef = Napi::Persistent(info[0].As<Napi::Function>());+ jsFn = info[1].As<Napi::Function>();+}++void NativeAddon::TryCallByStoredReference(const Napi::CallbackInfo& info) {+ // Napi::Env env = info.Env();+ jsFnRef.Call({});+}++void NativeAddon::TryCallByStoredFunction(const Napi::CallbackInfo& info) {+ // Napi::Env env = info.Env();+ jsFn.Call({});+}
src/4-references-and-handle-scope/function-reference-demo/node-addon-api/src/native-addon.h
@@ -0,0 +1,15 @@+#include <napi.h>++class NativeAddon : public Napi::ObjectWrap<NativeAddon> {+ public:+ static Napi::Object Init(Napi::Env env, Napi::Object exports);+ NativeAddon(const Napi::CallbackInfo& info);++ private:+ static Napi::FunctionReference constructor;+ Napi::FunctionReference jsFnRef;+ Napi::Function jsFn;++ void TryCallByStoredReference(const Napi::CallbackInfo& info);+ void TryCallByStoredFunction(const Napi::CallbackInfo& info);+};
src/5-async-work/async-iterator/node-addon-api/CMakeLists.txt
@@ -0,0 +1,23 @@+project (example)+include_directories(${CMAKE_JS_INC} node_modules/node-addon-api/)+cmake_minimum_required(VERSION 3.18)++set(CMAKE_CXX_STANDARD 20)+set(CMAKE_CXX_STANDARD_REQUIRED ON)+include_directories(${CMAKE_JS_INC})+file(GLOB SOURCE_FILES "*.cc")+add_library(${PROJECT_NAME} SHARED ${SOURCE_FILES} ${CMAKE_JS_SRC})+set_target_properties(${PROJECT_NAME} PROPERTIES PREFIX "" SUFFIX ".node")+target_link_libraries(${PROJECT_NAME} ${CMAKE_JS_LIB})++# Include Node-API wrappers+execute_process(COMMAND node -p "require('node-addon-api').include"+ WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}+ OUTPUT_VARIABLE NODE_ADDON_API_DIR+ )+string(REGEX REPLACE "[\r\n\"]" "" NODE_ADDON_API_DIR ${NODE_ADDON_API_DIR})++target_include_directories(${PROJECT_NAME} PRIVATE ${NODE_ADDON_API_DIR})++# define NAPI_VERSION+add_definitions(-DNAPI_VERSION=6)
src/5-async-work/async-iterator/node-addon-api/example.cc
@@ -0,0 +1,84 @@+#include <napi.h>+#include <memory>++using namespace Napi;++class AsyncIteratorExample : public ObjectWrap<AsyncIteratorExample> {+ public:+ AsyncIteratorExample(const CallbackInfo& info)+ : ObjectWrap<AsyncIteratorExample>(info),+ _from(info[0].As<Number>()),+ _to(info[1].As<Number>()) {}++ static Object Init(Napi::Env env, Napi::Object exports) {+ Napi::Function func = DefineClass(+ env,+ "AsyncIteratorExample",+ {InstanceMethod(Napi::Symbol::WellKnown(env, "asyncIterator"),+ &AsyncIteratorExample::Iterator)});++ exports.Set("AsyncIteratorExample", func);+ return exports;+ }++ Napi::Value Iterator(const CallbackInfo& info) {+ auto env = info.Env();+ auto iteratorObject = Napi::Object::New(env);++ iteratorObject["current"] = Number::New(env, _from);+ iteratorObject["last"] = Number::New(env, _to);+ auto next = Function::New(env, [](const CallbackInfo& info) {+ auto env = info.Env();+ auto deferred =+ std::make_shared<Promise::Deferred>(Promise::Deferred::New(env));+ auto iteratorObject = info.This().As<Object>();+ auto callback = Function::New(+ env,+ [=](const CallbackInfo& info) {+ auto env = info.Env();+ auto value = Object::New(env);+ auto iteratorObject = info.This().As<Object>();++ auto current =+ iteratorObject.Get("current").As<Number>().Int32Value();+ auto last = iteratorObject.Get("last").As<Number>().Int32Value();+ auto done = current > last;++ if (done) {+ value["done"] = Boolean::New(env, true);+ } else {+ value["done"] = Boolean::New(env, false);+ value["value"] = Number::New(env, current);+ iteratorObject["current"] = Number::New(env, current + 1);+ }+ deferred->Resolve(value);+ },+ "next");++ env.Global()+ .Get("setTimeout")+ .As<Function>()+ .Call({callback.Get("bind").As<Function>().Call(callback,+ {iteratorObject}),+ Number::New(env, 1000)});++ return deferred->Promise();+ });++ iteratorObject["next"] =+ next.Get("bind").As<Function>().Call(next, {iteratorObject});++ return iteratorObject;+ }++ private:+ int _from;+ int _to;+};++Napi::Object Init(Napi::Env env, Object exports) {+ AsyncIteratorExample::Init(env, exports);+ return exports;+}++NODE_API_MODULE(example, Init)
src/5-async-work/async-iterator/node-addon-api/index.js
@@ -0,0 +1,49 @@+const { AsyncIteratorExample } = require('bindings')('example');++async function main(from, to) {+ const iterator = new AsyncIteratorExample(from, to);+ for await (const value of iterator) {+ console.log(value);+ }+}++/*+// The JavaScript equivalent of the node-addon-api C++ code for reference+async function main(from, to) {+ class AsyncIteratorExample {+ constructor(from, to) {+ this.from = from;+ this.to = to;+ }++ [Symbol.asyncIterator]() {+ return {+ current: this.from,+ last: this.to,+ next() {+ return new Promise(resolve => {+ setTimeout(() => {+ if (this.current <= this.last) {+ resolve({ done: false, value: this.current++ });+ } else {+ resolve({ done: true });+ }+ }, 1000)+ });+ }+ }+ }+ }+ const iterator = new AsyncIteratorExample(from, to);++ for await (const value of iterator) {+ console.log(value);+ }+}+*/++main(0, 5)+ .catch(e => {+ console.error(e);+ process.exit(1);+ });
src/5-async-work/async-iterator/node-addon-api/package.json
@@ -0,0 +1,16 @@+{+ "name": "async-iterator-example",+ "version": "0.0.0",+ "description": "Async iterator example using node-addon-api",+ "main": "index.js",+ "private": true,+ "dependencies": {+ "bindings": "^1.5.0",+ "cmake-js": "^6.3.0",+ "node-addon-api": "^5.0.0"+ },+ "scripts": {+ "test": "node index.js",+ "install": "cmake-js compile"+ }+}
src/5-async-work/async_pi_estimate/nan/README.md
@@ -0,0 +1 @@+In this directory run `node-gyp rebuild` and then `node ./addon.js`
src/5-async-work/async_pi_estimate/nan/addon.cc
@@ -0,0 +1,24 @@+#include <nan.h>+#include "async.h" // NOLINT(build/include)+#include "sync.h" // NOLINT(build/include)++using Nan::GetFunction;+using Nan::New;+using Nan::Set;+using v8::FunctionTemplate;+using v8::Object;+using v8::String;++// Expose synchronous and asynchronous access to our+// Estimate() function+NAN_MODULE_INIT(InitAll) {+ Set(target,+ New<String>("calculateSync").ToLocalChecked(),+ GetFunction(New<FunctionTemplate>(CalculateSync)).ToLocalChecked());++ Set(target,+ New<String>("calculateAsync").ToLocalChecked(),+ GetFunction(New<FunctionTemplate>(CalculateAsync)).ToLocalChecked());+}++NODE_MODULE(addon, InitAll)
src/5-async-work/async_pi_estimate/nan/addon.js
@@ -0,0 +1,44 @@+var addon = require('./build/Release/addon');+var calculations = process.argv[2] || 100000000;++function printResult(type, pi, ms) {+ console.log(type, 'method:');+ console.log('\tπ ≈ ' + pi ++ ' (' + Math.abs(pi - Math.PI) + ' away from actual)');+ console.log('\tTook ' + ms + 'ms');+ console.log();+}++function runSync() {+ var start = Date.now();+ // Estimate() will execute in the current thread,+ // the next line won't return until it is finished+ var result = addon.calculateSync(calculations);+ printResult('Sync', result, Date.now() - start);+}++function runAsync() {+ // how many batches should we split the work in to?+ var batches = process.argv[3] || 16;+ var ended = 0;+ var total = 0;+ var start = Date.now();++ function done (err, result) {+ total += result;++ // have all the batches finished executing?+ if (++ended === batches) {+ printResult('Async', total / batches, Date.now() - start);+ }+ }++ // for each batch of work, request an async Estimate() for+ // a portion of the total number of calculations+ for (var i = 0; i < batches; i++) {+ addon.calculateAsync(calculations / batches, done);+ }+}++runSync();+runAsync();
src/5-async-work/async_pi_estimate/nan/async.cc
@@ -0,0 +1,51 @@+#include "async.h" // NOLINT(build/include)+#include <nan.h>+#include "pi_est.h" // NOLINT(build/include)++using Nan::AsyncQueueWorker;+using Nan::AsyncWorker;+using Nan::Callback;+using Nan::HandleScope;+using Nan::New;+using Nan::Null;+using Nan::To;+using v8::Function;+using v8::Local;+using v8::Number;+using v8::Value;++class PiWorker : public AsyncWorker {+ public:+ PiWorker(Callback* callback, int points)+ : AsyncWorker(callback), points(points), estimate(0) {}+ ~PiWorker() {}++ // Executed inside the worker-thread.+ // It is not safe to access V8, or V8 data structures+ // here, so everything we need for input and output+ // should go on `this`.+ void Execute() { estimate = Estimate(points); }++ // Executed when the async work is complete+ // this function will be run inside the main event loop+ // so it is safe to use V8 again+ void HandleOKCallback() {+ HandleScope scope;++ Local<Value> argv[] = {Null(), New<Number>(estimate)};++ callback->Call(2, argv, async_resource);+ }++ private:+ int points;+ double estimate;+};++// Asynchronous access to the `Estimate()` function+NAN_METHOD(CalculateAsync) {+ int points = To<int>(info[0]).FromJust();+ Callback* callback = new Callback(To<Function>(info[1]).ToLocalChecked());++ AsyncQueueWorker(new PiWorker(callback, points));+}
src/5-async-work/async_pi_estimate/nan/async.h
@@ -0,0 +1,8 @@+#ifndef EXAMPLES_ASYNC_PI_ESTIMATE_ASYNC_H_+#define EXAMPLES_ASYNC_PI_ESTIMATE_ASYNC_H_++#include <nan.h>++NAN_METHOD(CalculateAsync);++#endif // EXAMPLES_ASYNC_PI_ESTIMATE_ASYNC_H_
src/5-async-work/async_pi_estimate/nan/binding.gyp
@@ -0,0 +1,14 @@+{+ "targets": [+ {+ "target_name": "addon",+ "sources": [+ "addon.cc",+ "pi_est.cc",+ "sync.cc",+ "async.cc"+ ],+ "include_dirs": ["<!(node -e \"require('nan')\")"]+ }+ ]+}
src/5-async-work/async_pi_estimate/nan/package.json
@@ -0,0 +1,14 @@+{+ "name": "async_work",+ "version": "0.0.0",+ "description": "Node.js Addons Example #9",+ "main": "addon.js",+ "private": true,+ "gypfile": true,+ "scripts": {+ "test": "node addon.js"+ },+ "dependencies": {+ "nan": "*"+ }+}
src/5-async-work/async_pi_estimate/nan/pi_est.cc
@@ -0,0 +1,54 @@+#include "pi_est.h" // NOLINT(build/include)+#include <cstdlib>++/*+Estimate the value of π by using a Monte Carlo method.+Take `points` samples of random x and y values on a+[0,1][0,1] plane. Calculating the length of the diagonal+tells us whether the point lies inside, or outside a+quarter circle running from 0,1 to 1,0. The ratio of the+number of points inside to outside gives us an+approximation of π/4.++See https://en.wikipedia.org/wiki/File:Pi_30K.gif+for a visualization of how this works.+*/++inline int randall(unsigned int* p_seed) {+// windows has thread safe rand()+#ifdef _WIN32+ return rand(); // NOLINT(runtime/threadsafe_fn)+#else+ return rand_r(p_seed);+#endif+}++double Estimate(int points) {+ int i = points;+ int inside = 0;+ unsigned int randseed = 1;++#ifdef _WIN32+ srand(randseed);+#endif++ // unique seed for each run, for threaded use+ unsigned int seed = randall(&randseed);++#ifdef _WIN32+ srand(seed);+#endif++ while (i-- > 0) {+ double x = randall(&seed) / static_cast<double>(RAND_MAX);+ double y = randall(&seed) / static_cast<double>(RAND_MAX);++ // x & y and now values between 0 and 1+ // now do a pythagorean diagonal calculation+ // `1` represents our 1/4 circle+ if ((x * x) + (y * y) <= 1) inside++;+ }++ // calculate ratio and multiply by 4 for π+ return (inside / static_cast<double>(points)) * 4;+}
src/5-async-work/async_pi_estimate/nan/pi_est.h
@@ -0,0 +1,6 @@+#ifndef EXAMPLES_ASYNC_PI_ESTIMATE_PI_EST_H_+#define EXAMPLES_ASYNC_PI_ESTIMATE_PI_EST_H_++double Estimate(int points);++#endif // EXAMPLES_ASYNC_PI_ESTIMATE_PI_EST_H_
src/5-async-work/async_pi_estimate/nan/sync.cc
@@ -0,0 +1,12 @@+#include "sync.h" // NOLINT(build/include)+#include <nan.h>+#include "pi_est.h" // NOLINT(build/include)++// Simple synchronous access to the `Estimate()` function+NAN_METHOD(CalculateSync) {+ // expect a number as the first argument+ int points = info[0]->Uint32Value(Nan::GetCurrentContext()).FromJust();+ double est = Estimate(points);++ info.GetReturnValue().Set(est);+}
src/5-async-work/async_pi_estimate/nan/sync.h
@@ -0,0 +1,8 @@+#ifndef EXAMPLES_ASYNC_PI_ESTIMATE_SYNC_H_+#define EXAMPLES_ASYNC_PI_ESTIMATE_SYNC_H_++#include <nan.h>++NAN_METHOD(CalculateSync);++#endif // EXAMPLES_ASYNC_PI_ESTIMATE_SYNC_H_
src/5-async-work/async_pi_estimate/node-addon-api/README.md
@@ -0,0 +1 @@+In this directory run `node-gyp rebuild` and then `node ./addon.js`
src/5-async-work/async_pi_estimate/node-addon-api/addon.cc
@@ -0,0 +1,15 @@+#include <napi.h>+#include "async.h" // NOLINT(build/include)+#include "sync.h" // NOLINT(build/include)++// Expose synchronous and asynchronous access to our+// Estimate() function+Napi::Object Init(Napi::Env env, Napi::Object exports) {+ exports.Set(Napi::String::New(env, "calculateSync"),+ Napi::Function::New(env, CalculateSync));+ exports.Set(Napi::String::New(env, "calculateAsync"),+ Napi::Function::New(env, CalculateAsync));+ return exports;+}++NODE_API_MODULE(NODE_GYP_MODULE_NAME, Init)
src/5-async-work/async_pi_estimate/node-addon-api/addon.js
@@ -0,0 +1,44 @@+var addon = require('bindings')('addon');+var calculations = process.argv[2] || 100000000;++function printResult(type, pi, ms) {+ console.log(type, 'method:');+ console.log('\tπ ≈ ' + pi ++ ' (' + Math.abs(pi - Math.PI) + ' away from actual)');+ console.log('\tTook ' + ms + 'ms');+ console.log();+}++function runSync() {+ var start = Date.now();+ // Estimate() will execute in the current thread,+ // the next line won't return until it is finished+ var result = addon.calculateSync(calculations);+ printResult('Sync', result, Date.now() - start);+}++function runAsync() {+ // how many batches should we split the work in to?+ var batches = process.argv[3] || 16;+ var ended = 0;+ var total = 0;+ var start = Date.now();++ function done (err, result) {+ total += result;++ // have all the batches finished executing?+ if (++ended === batches) {+ printResult('Async', total / batches, Date.now() - start);+ }+ }++ // for each batch of work, request an async Estimate() for+ // a portion of the total number of calculations+ for (var i = 0; i < batches; i++) {+ addon.calculateAsync(calculations / batches, done);+ }+}++runSync();+runAsync();
src/5-async-work/async_pi_estimate/node-addon-api/async.cc
@@ -0,0 +1,36 @@+#include "async.h" // NOLINT(build/include)+#include <napi.h>+#include "pi_est.h" // NOLINT(build/include)++class PiWorker : public Napi::AsyncWorker {+ public:+ PiWorker(Napi::Function& callback, int points)+ : Napi::AsyncWorker(callback), points(points), estimate(0) {}+ ~PiWorker() {}++ // Executed inside the worker-thread.+ // It is not safe to access JS engine data structure+ // here, so everything we need for input and output+ // should go on `this`.+ void Execute() { estimate = Estimate(points); }++ // Executed when the async work is complete+ // this function will be run inside the main event loop+ // so it is safe to use JS engine data again+ void OnOK() {+ Callback().Call({Env().Undefined(), Napi::Number::New(Env(), estimate)});+ }++ private:+ int points;+ double estimate;+};++// Asynchronous access to the `Estimate()` function+Napi::Value CalculateAsync(const Napi::CallbackInfo& info) {+ int points = info[0].As<Napi::Number>().Uint32Value();+ Napi::Function callback = info[1].As<Napi::Function>();+ PiWorker* piWorker = new PiWorker(callback, points);+ piWorker->Queue();+ return info.Env().Undefined();+}
src/5-async-work/async_pi_estimate/node-addon-api/async.h
@@ -0,0 +1,8 @@+#ifndef EXAMPLES_ASYNC_PI_ESTIMATE_ASYNC_H_+#define EXAMPLES_ASYNC_PI_ESTIMATE_ASYNC_H_++#include <napi.h>++Napi::Value CalculateAsync(const Napi::CallbackInfo& info);++#endif // EXAMPLES_ASYNC_PI_ESTIMATE_ASYNC_H_
src/5-async-work/async_pi_estimate/node-addon-api/binding.gyp
@@ -0,0 +1,33 @@+{+ "targets": [+ {+ "target_name": "addon",+ "sources": [+ "addon.cc",+ "pi_est.cc",+ "sync.cc",+ "async.cc"+ ],+ 'cflags!': [ '-fno-exceptions' ],+ 'cflags_cc!': [ '-fno-exceptions' ],+ 'include_dirs': ["<!@(node -p \"require('node-addon-api').include\")"],+ 'dependencies': ["<!(node -p \"require('node-addon-api').gyp\")"],+ 'conditions': [+ ['OS=="win"', {+ "msvs_settings": {+ "VCCLCompilerTool": {+ "ExceptionHandling": 1+ }+ }+ }],+ ['OS=="mac"', {+ "xcode_settings": {+ "CLANG_CXX_LIBRARY": "libc++",+ 'GCC_ENABLE_CPP_EXCEPTIONS': 'YES',+ 'MACOSX_DEPLOYMENT_TARGET': '10.7'+ }+ }]+ ]+ }+ ]+}
src/5-async-work/async_pi_estimate/node-addon-api/package.json
@@ -0,0 +1,15 @@+{+ "name": "async_work",+ "version": "0.0.0",+ "description": "Node.js Addons Example #9",+ "main": "addon.js",+ "private": true,+ "gypfile": true,+ "scripts": {+ "test": "node addon.js"+ },+ "dependencies": {+ "node-addon-api": "*",+ "bindings": "*"+ }+}
src/5-async-work/async_pi_estimate/node-addon-api/pi_est.cc
@@ -0,0 +1,54 @@+#include "pi_est.h" // NOLINT(build/include)+#include <cstdlib>++/*+Estimate the value of π by using a Monte Carlo method.+Take `points` samples of random x and y values on a+[0,1][0,1] plane. Calculating the length of the diagonal+tells us whether the point lies inside, or outside a+quarter circle running from 0,1 to 1,0. The ratio of the+number of points inside to outside gives us an+approximation of π/4.++See https://en.wikipedia.org/wiki/File:Pi_30K.gif+for a visualization of how this works.+*/++inline int randall(unsigned int* p_seed) {+// windows has thread safe rand()+#ifdef _WIN32+ return rand(); // NOLINT(runtime/threadsafe_fn)+#else+ return rand_r(p_seed);+#endif+}++double Estimate(int points) {+ int i = points;+ int inside = 0;+ unsigned int randseed = 1;++#ifdef _WIN32+ srand(randseed);+#endif++ // unique seed for each run, for threaded use+ unsigned int seed = randall(&randseed);++#ifdef _WIN32+ srand(seed);+#endif++ while (i-- > 0) {+ double x = randall(&seed) / static_cast<double>(RAND_MAX);+ double y = randall(&seed) / static_cast<double>(RAND_MAX);++ // x & y and now values between 0 and 1+ // now do a pythagorean diagonal calculation+ // `1` represents our 1/4 circle+ if ((x * x) + (y * y) <= 1) inside++;+ }++ // calculate ratio and multiply by 4 for π+ return (inside / static_cast<double>(points)) * 4;+}
src/5-async-work/async_pi_estimate/node-addon-api/pi_est.h
@@ -0,0 +1,6 @@+#ifndef EXAMPLES_ASYNC_PI_ESTIMATE_PI_EST_H_+#define EXAMPLES_ASYNC_PI_ESTIMATE_PI_EST_H_++double Estimate(int points);++#endif // EXAMPLES_ASYNC_PI_ESTIMATE_PI_EST_H_
src/5-async-work/async_pi_estimate/node-addon-api/sync.cc
@@ -0,0 +1,12 @@+#include "sync.h" // NOLINT(build/include)+#include <napi.h>+#include "pi_est.h" // NOLINT(build/include)++// Simple synchronous access to the `Estimate()` function+Napi::Value CalculateSync(const Napi::CallbackInfo& info) {+ // expect a number as the first argument+ int points = info[0].As<Napi::Number>().Uint32Value();+ double est = Estimate(points);++ return Napi::Number::New(info.Env(), est);+}
src/5-async-work/async_pi_estimate/node-addon-api/sync.h
@@ -0,0 +1,8 @@+#ifndef EXAMPLES_ASYNC_PI_ESTIMATE_SYNC_H_+#define EXAMPLES_ASYNC_PI_ESTIMATE_SYNC_H_++#include <napi.h>++Napi::Value CalculateSync(const Napi::CallbackInfo& info);++#endif // EXAMPLES_ASYNC_PI_ESTIMATE_SYNC_H_
src/5-async-work/async_work_promise/napi/binding.c
@@ -0,0 +1,170 @@+#include <stdio.h>+#include <stdlib.h>+#include <node_api.h>++// Limit ourselves to this many primes, starting at 2+#define PRIME_COUNT 10+#define CHECK(expr) \+ { \+ if ((expr) == 0) { \+ fprintf(stderr, "%s:%d: failed assertion `%s'\n", __FILE__, __LINE__, #expr); \+ fflush(stderr); \+ abort(); \+ } \+ }++typedef struct linked_box_s linked_box_t;+struct linked_box_s {+ linked_box_t* prev;+ int the_prime;+};++typedef struct {+ napi_async_work work;+ napi_deferred deferred;+ int data[PRIME_COUNT];+} AddonData;++// This function runs on a worker thread. It has no access to the JavaScript.+static void ExecuteWork(napi_env env, void* data) {+ AddonData* addon_data = (AddonData*)data;+ int idx_inner, idx_outer;+ int prime_count = 0;++ // Find the first 1000 prime numbers using an extremely inefficient algorithm.+ for (idx_outer = 2; prime_count < PRIME_COUNT; idx_outer++) {+ for (idx_inner = 2; idx_inner < idx_outer; idx_inner++) {+ if (idx_outer % idx_inner == 0) {+ break;+ }+ }+ if (idx_inner < idx_outer) {+ continue;+ }++ // Save the prime number to the heap. The JavaScript marshaller (CallJs)+ // will free this item after having sent it to JavaScript.+ addon_data->data[prime_count] = idx_outer;+ ++prime_count;+ }+}++// This function runs on the main thread after `ExecuteWork` exits.+static void WorkComplete(napi_env env, napi_status status, void* data) {+ if (status != napi_ok) {+ return;+ }++ AddonData* addon_data = (AddonData*)data;+ napi_value array;+ CHECK(napi_create_array(env, &array) == napi_ok);++ for (uint32_t idx = 0; idx < PRIME_COUNT; idx++) {+ napi_value js_the_prime;++ // Convert the integer to a napi_value.+ CHECK(napi_create_int32(env, addon_data->data[idx], &js_the_prime) == napi_ok);++ // Store the js value in the array.+ CHECK(napi_set_element(env, array, idx, js_the_prime) == napi_ok);+ }+ CHECK(napi_resolve_deferred(env, addon_data->deferred, array) == napi_ok);++ // Clean up the work item associated with this run.+ CHECK(napi_delete_async_work(env, addon_data->work) == napi_ok);++ // Set both values to NULL so JavaScript can order a new run of the thread.+ addon_data->work = NULL;+ addon_data->deferred = NULL;+}++// Create a deferred promise and an async queue work item.+static napi_value StartWork(napi_env env, napi_callback_info info) {+ napi_value work_name, promise;+ AddonData* addon_data;++ // Retrieve the per-addon data.+ CHECK(napi_get_cb_info(env,+ info,+ NULL,+ NULL,+ NULL,+ (void**)(&addon_data)) == napi_ok);++ // Ensure that no work is currently in progress.+ CHECK(addon_data->work == NULL && "Only one work item must exist at a time");++ // Create a string to describe this asynchronous operation.+ CHECK(napi_create_string_utf8(env,+ "Node-API Deferred Promise from Async Work Item",+ NAPI_AUTO_LENGTH,+ &work_name) == napi_ok);++ // Create a deferred promise which we will resolve at the completion of the work.+ CHECK(napi_create_promise(env,+ &(addon_data->deferred),+ &promise) == napi_ok);++ // Create an async work item, passing in the addon data, which will give the+ // worker thread access to the above-created deferred promise.+ CHECK(napi_create_async_work(env,+ NULL,+ work_name,+ ExecuteWork,+ WorkComplete,+ addon_data,+ &(addon_data->work)) == napi_ok);++ // Queue the work item for execution.+ CHECK(napi_queue_async_work(env, addon_data->work) == napi_ok);++ // This causes created `promise` to be returned to JavaScript.+ return promise;+}++// Free the per-addon-instance data.+static void addon_getting_unloaded(napi_env env, void* data, void* hint) {+ AddonData* addon_data = (AddonData*)data;+ CHECK(addon_data->work == NULL &&+ "No work item in progress at module unload");+ free(addon_data);+}++// The commented-out return type and the commented out formal function+// parameters below help us keep in mind the signature of the addon+// initialization function. We write the body as though the return value were as+// commented below and as though there were parameters passed in as commented+// below.+/*napi_value*/ NAPI_MODULE_INIT(/*napi_env env, napi_value exports*/) {++ // Define addon-level data associated with this instance of the addon.+ AddonData* addon_data = (AddonData*)malloc(sizeof(*addon_data));+ addon_data->work = NULL;++ // Define the properties that will be set on exports.+ napi_property_descriptor start_work = {+ "startWork",+ NULL,+ StartWork,+ NULL,+ NULL,+ NULL,+ napi_default,+ addon_data+ };++ // Decorate exports with the above-defined properties.+ CHECK(napi_define_properties(env, exports, 1, &start_work) == napi_ok);++ // Associate the addon data with the exports object, to make sure that when+ // the addon gets unloaded our data gets freed.+ CHECK(napi_wrap(env,+ exports,+ addon_data,+ addon_getting_unloaded,+ NULL,+ NULL) == napi_ok);++ // Return the decorated exports object.+ return exports;+}
src/5-async-work/async_work_promise/napi/binding.gyp
@@ -0,0 +1,8 @@+{+ 'targets': [+ {+ 'target_name': 'binding',+ 'sources': [ 'binding.c' ]+ }+ ]+}
src/5-async-work/async_work_promise/napi/index.js
@@ -0,0 +1,10 @@+// Use the "bindings" package to locate the native bindings.+const binding = require('bindings')('binding');++// Call the function "startWork" which the native bindings library exposes.+// The function returns a promise which will be resolved at the complete of the+// work with an array of worked out primes. This resolution simply prints them out.+binding.startWork()+ .then((thePrimes) => {+ console.log("Received primes from completed work: " + thePrimes)+ });
src/5-async-work/async_work_promise/napi/package.json
@@ -0,0 +1,17 @@+{+ "name": "async_work_promise",+ "version": "0.0.0",+ "description": "Calling into JS from the thread pool",+ "main": "index.js",+ "private": true,+ "dependencies": {+ "bindings": "~1.5.0"+ },+ "engines": {+ "node": ">= 10.6.0"+ },+ "scripts": {+ "test": "node index.js"+ },+ "gypfile": true+}
src/5-async-work/async_work_promise/node-addon-api/addon.cc
@@ -0,0 +1,32 @@+#include "napi.h"+#include "worker.h"++Napi::Value DoHeavyMath(const Napi::CallbackInfo& info) {+ Napi::Env env = info.Env();++ if (!info[0].IsNumber()) {+ Napi::TypeError::New(env, "num1 must be a number")+ .ThrowAsJavaScriptException();+ return env.Undefined();+ }+ uint32_t num_1 = info[0].As<Napi::Number>().Uint32Value();++ if (!info[1].IsNumber()) {+ Napi::TypeError::New(env, "num2 must be a number")+ .ThrowAsJavaScriptException();+ return env.Undefined();+ }+ uint32_t num_2 = info[1].As<Napi::Number>().Uint32Value();++ DoHeavyMathWorker* worker = new DoHeavyMathWorker(env, num_1, num_2);+ worker->Queue();+ return worker->GetPromise();+}++Napi::Object Init(Napi::Env env, Napi::Object exports) {+ exports.Set(Napi::String::New(env, "doHeavyMath"),+ Napi::Function::New(env, DoHeavyMath));+ return exports;+}++NODE_API_MODULE(NODE_GYP_MODULE_NAME, Init)
src/5-async-work/async_work_promise/node-addon-api/binding.gyp
@@ -0,0 +1,32 @@+{+ "targets": [{+ "target_name": "addon",+ "sources": [+ "addon.cc"+ ],+ "include_dirs": [+ "<!@(node -p \"require('node-addon-api').include\")"+ ],+ 'defines': [ 'NAPI_CPP_EXCEPTIONS' ],+ 'conditions': [+ [ 'OS=="win"', {+ 'defines': [ '_HAS_EXCEPTIONS=1' ],+ 'msvs_settings': {+ 'VCCLCompilerTool': {+ 'ExceptionHandling': 1,+ },+ },+ }],+ [ 'OS=="linux"', {+ "cflags": [ "-fexceptions" ],+ "cflags_cc": [ "-fexceptions" ],+ }],+ [ 'OS=="mac"', {+ 'xcode_settings': {+ 'GCC_ENABLE_CPP_EXCEPTIONS': 'YES',+ 'CLANG_CXX_LIBRARY': 'libc++',+ }+ }]+ ],+ }]+}
src/5-async-work/async_work_promise/node-addon-api/index.js
@@ -0,0 +1,3 @@+const bindings = require("bindings")("addon");++bindings.doHeavyMath(2, 1).then(console.log);
src/5-async-work/async_work_promise/node-addon-api/package.json
@@ -0,0 +1,15 @@+{+ "name": "async_worker_promise",+ "version": "0.0.0",+ "description": "AsyncWorker example that returns a Promise",+ "main": "index.js",+ "scripts": {+ "test": "node ."+ },+ "author": "Matthew Keil (matthewkeil)",+ "gypfile": true,+ "dependencies": {+ "bindings": "^1.5.0",+ "node-addon-api": "^7.0.0"+ }+}
src/5-async-work/async_work_promise/node-addon-api/worker.h
@@ -0,0 +1,43 @@+#include "napi.h"++class DoHeavyMathWorker : public Napi::AsyncWorker {+ public:+ DoHeavyMathWorker(const Napi::Env& env, uint32_t num_1, uint32_t num_2)+ : Napi::AsyncWorker{env, "DoHeavyMathWorker"},+ m_deferred{env},+ m_num_1{num_1},+ m_num_2{num_2} {}++ /**+ * GetPromise associated with _deferred for return to JS+ */+ Napi::Promise GetPromise() { return m_deferred.Promise(); }++ protected:+ /**+ * Simulate heavy math work+ */+ void Execute() {+ if (m_num_2 == 0) {+ SetError("Cannot divide by zero");+ return;+ }+ m_result = m_num_1 / m_num_2;+ }++ /**+ * Resolve the promise with the result+ */+ void OnOK() { m_deferred.Resolve(Napi::Number::New(Env(), m_result)); }++ /**+ * Reject the promise with errors+ */+ void OnError(const Napi::Error& err) { m_deferred.Reject(err.Value()); }++ private:+ Napi::Promise::Deferred m_deferred;+ uint32_t m_num_1;+ uint32_t m_num_2;+ uint32_t m_result;+};
src/5-async-work/async_work_thread_safe_function/napi/binding.c
@@ -0,0 +1,235 @@+#include <assert.h>+#include <stdlib.h>+#define NAPI_EXPERIMENTAL+#include <node_api.h>++// Limit ourselves to this many primes, starting at 2+#define PRIME_COUNT 100000+#define REPORT_EVERY 1000++typedef struct {+ napi_async_work work;+ napi_threadsafe_function tsfn;+} AddonData;+++// This function is responsible for converting data coming in from the worker+// thread to napi_value items that can be passed into JavaScript, and for+// calling the JavaScript function.+static void CallJs(napi_env env, napi_value js_cb, void* context, void* data) {+ // This parameter is not used.+ (void) context;+ napi_status status;++ // Retrieve the prime from the item created by the worker thread.+ int the_prime = *(int*)data;++ // env and js_cb may both be NULL if Node.js is in its cleanup phase, and+ // items are left over from earlier thread-safe calls from the worker thread.+ // When env is NULL, we simply skip over the call into Javascript and free the+ // items.+ if (env != NULL) {+ napi_value undefined, js_the_prime;++ // Convert the integer to a napi_value.+ status = napi_create_int32(env, the_prime, &js_the_prime);+ assert(status == napi_ok);++ // Retrieve the JavaScript `undefined` value so we can use it as the `this`+ // value of the JavaScript function call.+ status = napi_get_undefined(env, &undefined);+ assert(status == napi_ok);++ // Call the JavaScript function and pass it the prime that the secondary+ // thread found.+ status = napi_call_function(env,+ undefined,+ js_cb,+ 1,+ &js_the_prime,+ NULL);+ assert(status == napi_ok);+ }++ // Free the item created by the worker thread.+ free(data);+}++// This function runs on a worker thread. It has no access to the JavaScript+// environment except through the thread-safe function.+static void ExecuteWork(napi_env env, void* data) {+ AddonData* addon_data = (AddonData*)data;+ int idx_inner, idx_outer;+ int prime_count = 0;+ napi_status status;++ // We bracket the use of the thread-safe function by this thread by a call to+ // napi_acquire_threadsafe_function() here, and by a call to+ // napi_release_threadsafe_function() immediately prior to thread exit.+ status = napi_acquire_threadsafe_function(addon_data->tsfn);+ assert(status == napi_ok);++ // Find the first 1000 prime numbers using an extremely inefficient algorithm.+ for (idx_outer = 2; prime_count < PRIME_COUNT; idx_outer++) {+ for (idx_inner = 2; idx_inner < idx_outer; idx_inner++) {+ if (idx_outer % idx_inner == 0) {+ break;+ }+ }+ if (idx_inner < idx_outer) {+ continue;+ }++ // We found a prime. If it's the tenth since the last time we sent one to+ // JavaScript, send it to JavaScript.+ if (!(++prime_count % REPORT_EVERY)) {++ // Save the prime number to the heap. The JavaScript marshaller (CallJs)+ // will free this item after having sent it to JavaScript.+ int* the_prime = malloc(sizeof(*the_prime));+ *the_prime = idx_outer;++ // Initiate the call into JavaScript. The call into JavaScript will not+ // have happened when this function returns, but it will be queued.+ status = napi_call_threadsafe_function(addon_data->tsfn,+ the_prime,+ napi_tsfn_blocking);+ assert(status == napi_ok);+ }+ }++ // Indicate that this thread will make no further use of the thread-safe function.+ status = napi_release_threadsafe_function(addon_data->tsfn,+ napi_tsfn_release);+ assert(status == napi_ok);+}++// This function runs on the main thread after `ExecuteWork` exits.+static void WorkComplete(napi_env env, napi_status status, void* data) {+ AddonData* addon_data = (AddonData*)data;++ // Clean up the thread-safe function and the work item associated with this+ // run.+ status = napi_release_threadsafe_function(addon_data->tsfn,+ napi_tsfn_release);+ assert(status == napi_ok);+ status = napi_delete_async_work(env, addon_data->work);+ assert(status == napi_ok);++ // Set both values to NULL so JavaScript can order a new run of the thread.+ addon_data->work = NULL;+ addon_data->tsfn = NULL;+}++// Create a thread-safe function and an async queue work item. We pass the+// thread-safe function to the async queue work item so the latter might have a+// chance to call into JavaScript from the worker thread on which the+// ExecuteWork callback runs.+static napi_value StartThread(napi_env env, napi_callback_info info) {+ size_t argc = 1;+ napi_value js_cb, work_name;+ AddonData* addon_data;+ napi_status status;++ // Retrieve the JavaScript callback we should call with items generated by the+ // worker thread, and the per-addon data.+ status = napi_get_cb_info(env,+ info,+ &argc,+ &js_cb,+ NULL,+ (void**)(&addon_data));+ assert(status == napi_ok);++ // Ensure that no work is currently in progress.+ assert(addon_data->work == NULL && "Only one work item must exist at a time");++ // Create a string to describe this asynchronous operation.+ status = napi_create_string_utf8(env,+ "Node-API Thread-safe Call from Async Work Item",+ NAPI_AUTO_LENGTH,+ &work_name);+ assert(status == napi_ok);++ // Convert the callback retrieved from JavaScript into a thread-safe function+ // which we can call from a worker thread.+ status = napi_create_threadsafe_function(env,+ js_cb,+ NULL,+ work_name,+ 0,+ 1,+ NULL,+ NULL,+ NULL,+ CallJs,+ &(addon_data->tsfn));+ assert(status == napi_ok);++ // Create an async work item, passing in the addon data, which will give the+ // worker thread access to the above-created thread-safe function.+ status = napi_create_async_work(env,+ NULL,+ work_name,+ ExecuteWork,+ WorkComplete,+ addon_data,+ &(addon_data->work));+ assert(status == napi_ok);++ // Queue the work item for execution.+ status = napi_queue_async_work(env, addon_data->work);+ assert(status == napi_ok);++ // This causes `undefined` to be returned to JavaScript.+ return NULL;+}++// Free the per-addon-instance data.+static void addon_getting_unloaded(napi_env env, void* data, void* hint) {+ AddonData* addon_data = (AddonData*)data;+ assert(addon_data->work == NULL &&+ "No work item in progress at module unload");+ free(addon_data);+}++// The commented-out return type and the commented out formal function+// parameters below help us keep in mind the signature of the addon+// initialization function. We write the body as though the return value were as+// commented below and as though there were parameters passed in as commented+// below.+/*napi_value*/ NAPI_MODULE_INIT(/*napi_env env, napi_value exports*/) {+ napi_status status;+ // Define addon-level data associated with this instance of the addon.+ AddonData* addon_data = (AddonData*)malloc(sizeof(*addon_data));+ addon_data->work = NULL;++ // Define the properties that will be set on exports.+ napi_property_descriptor start_work = {+ "startThread",+ NULL,+ StartThread,+ NULL,+ NULL,+ NULL,+ napi_default,+ addon_data+ };++ // Decorate exports with the above-defined properties.+ status = napi_define_properties(env, exports, 1, &start_work);+ assert(status == napi_ok);++ // Associate the addon data with the exports object, to make sure that when+ // the addon gets unloaded our data gets freed.+ status = napi_wrap(env,+ exports,+ addon_data,+ addon_getting_unloaded,+ NULL,+ NULL);+ assert(status == napi_ok);++ // Return the decorated exports object.+ return exports;+}
src/5-async-work/async_work_thread_safe_function/napi/binding.gyp
@@ -0,0 +1,8 @@+{+ 'targets': [+ {+ 'target_name': 'binding',+ 'sources': [ 'binding.c' ]+ }+ ]+}
src/5-async-work/async_work_thread_safe_function/napi/index.js
@@ -0,0 +1,8 @@+// Use the "bindings" package to locate the native bindings.+const binding = require('bindings')('binding');++// Call the function "startThread" which the native bindings library exposes.+// The function accepts a callback which it will call from the worker thread and+// into which it will pass prime numbers. This callback simply prints them out.+binding.startThread((thePrime) =>+ console.log("Received prime from secondary thread: " + thePrime));
src/5-async-work/async_work_thread_safe_function/napi/package.json
@@ -0,0 +1,17 @@+{+ "name": "async_work_thread_safe_function",+ "version": "0.0.0",+ "description": "Calling into JS from the thread pool",+ "main": "index.js",+ "private": true,+ "dependencies": {+ "bindings": "~1.2.1"+ },+ "engines": {+ "node": ">= 10.6.0"+ },+ "scripts": {+ "test": "node index.js"+ },+ "gypfile": true+}
src/5-async-work/call-js-from-async-worker-execute/node-addon-api/binding.gyp
@@ -0,0 +1,28 @@+{+ "targets": [+ {+ "target_name": "dispatcher",+ "sources": [ "src/binding.cc" ],+ 'cflags!': [ '-fno-exceptions' ],+ 'cflags_cc!': [ '-fno-exceptions' ],+ 'include_dirs': ["<!@(node -p \"require('node-addon-api').include\")"],+ 'dependencies': ["<!(node -p \"require('node-addon-api').gyp\")"],+ 'conditions': [+ ['OS=="win"', {+ "msvs_settings": {+ "VCCLCompilerTool": {+ "ExceptionHandling": 1+ }+ }+ }],+ ['OS=="mac"', {+ "xcode_settings": {+ "CLANG_CXX_LIBRARY": "libc++",+ 'GCC_ENABLE_CPP_EXCEPTIONS': 'YES',+ 'MACOSX_DEPLOYMENT_TARGET': '10.7'+ }+ }]+ ]+ }+ ]+}
src/5-async-work/call-js-from-async-worker-execute/node-addon-api/index.js
@@ -0,0 +1,25 @@+'use strict'++const { EventEmitter } = require('node:events');+const { dispatch } = require('bindings')('dispatcher');++class Socket extends EventEmitter {+ constructor () {+ super();+ dispatch(this.emit.bind(this));+ }+}++const socket = new Socket();++socket.on('open', () => {+ console.log('opened');+});++socket.on('message', (message) => {+ console.log(`message: ${message}`);+});++socket.on('close', () => {+ console.log('closed');+});
src/5-async-work/call-js-from-async-worker-execute/node-addon-api/package.json
@@ -0,0 +1,15 @@+{+ "name": "call-js-from-async-worker-execute",+ "version": "0.0.0",+ "description": "Node.js Addons - calls JS from AsyncWorker::Execute",+ "main": "index.js",+ "private": true,+ "gypfile": true,+ "scripts": {+ "start": "node index.js"+ },+ "dependencies": {+ "node-addon-api": "*",+ "bindings": "*"+ }+}
src/5-async-work/call-js-from-async-worker-execute/node-addon-api/src/binding.cc
@@ -0,0 +1,101 @@+#include <napi.h>++#include <chrono>+#include <thread>++class Dispatcher : public Napi::AsyncWorker {+ public:+ Dispatcher(Napi::Env env, Napi::Function& callback)+ : Napi::AsyncWorker(env),+ callback_(callback),+ tsfn_{+ Napi::ThreadSafeFunction::New(env, callback, "Dispatcher", 0, 1)} {}++ virtual ~Dispatcher() override { tsfn_.Release(); }++ void Execute() override {+ // Since this method executes on a thread that is different from the main+ // thread, we can't directly call into JavaScript. To trigger a call into+ // JavaScript from this thread, ThreadSafeFunction needs to be used to+ // communicate with the main thread, so that the main thread can invoke the+ // JavaScript function.++ napi_status status =+ tsfn_.BlockingCall([](Napi::Env env, Napi::Function js_callback) {+ js_callback.Call({Napi::String::New(env, "open")});+ });+ if (status != napi_ok) {+ SetError("Napi::ThreadSafeNapi::Function.BlockingCall() failed");+ return;+ }++ std::this_thread::sleep_for(std::chrono::seconds(1));+ status = tsfn_.BlockingCall([](Napi::Env env, Napi::Function js_callback) {+ js_callback.Call(+ {Napi::String::New(env, "message"), Napi::String::New(env, "data1")});+ });+ if (status != napi_ok) {+ SetError("Napi::ThreadSafeNapi::Function.BlockingCall() failed");+ return;+ }++ std::this_thread::sleep_for(std::chrono::seconds(1));+ status = tsfn_.BlockingCall([](Napi::Env env, Napi::Function js_callback) {+ js_callback.Call(+ {Napi::String::New(env, "message"), Napi::String::New(env, "data2")});+ });+ if (status != napi_ok) {+ SetError("Napi::ThreadSafeNapi::Function.BlockingCall() failed");+ return;+ }++ std::this_thread::sleep_for(std::chrono::seconds(1));+ status = tsfn_.BlockingCall([](Napi::Env env, Napi::Function js_callback) {+ js_callback.Call(+ {Napi::String::New(env, "message"), Napi::String::New(env, "data3")});+ });+ if (status != napi_ok) {+ SetError("Napi::ThreadSafeNapi::Function.BlockingCall() failed");+ return;+ }++ status = tsfn_.BlockingCall([](Napi::Env env, Napi::Function js_callback) {+ js_callback.Call({Napi::String::New(env, "close")});+ });+ if (status != napi_ok) {+ SetError("Napi::ThreadSafeNapi::Function.BlockingCall() failed");+ return;+ }+ }++ void OnError(Napi::Error const& error) override {+ callback_.Call({Napi::String::New(Env(), "error"), error.Value()});+ }++ private:+ Napi::Function callback_;+ Napi::ThreadSafeFunction tsfn_;+};++void Dispatch(const Napi::CallbackInfo& info) {+ Napi::Env env = info.Env();++ if (info.Length() < 1 || !info[0].IsFunction()) {+ Napi::TypeError::New(+ env, "The first argument needs to be the callback function.")+ .ThrowAsJavaScriptException();+ return;+ }++ Napi::Function callback = info[0].As<Napi::Function>();++ Dispatcher* dispatcher = new Dispatcher(env, callback);+ dispatcher->Queue();+}++Napi::Object Init(Napi::Env env, Napi::Object exports) {+ exports.Set("dispatch", Napi::Function::New(env, Dispatch));+ return exports;+}++NODE_API_MODULE(dispatcher, Init)
src/5-async-work/napi-asyncworker-example/node-addon-api/README.md
@@ -0,0 +1,14 @@+# Node-API AsyncWorker Example++This is an example project showing how to use the Node.js Node-API AsyncWorker class++A tutorial describing this project can be found at the [Node-API Resource](https://napi.inspiredware.com/special-topics/asyncworker.html).++To build and run this program on your system, clone it to your computer and run these two commands inside your clone:++```+npm install+npm test+```++> You need to have Node 10.5.0 or later installed.
src/5-async-work/napi-asyncworker-example/node-addon-api/binding.gyp
@@ -0,0 +1,20 @@+{ + 'targets': [ + { + 'target_name': 'napi-asyncworker-example-native', + 'sources': [ 'src/RunSimpleAsyncWorker.cc', 'src/SimpleAsyncWorker.cc' ], + 'include_dirs': ["<!@(node -p \"require('node-addon-api').include\")"], + 'dependencies': ["<!(node -p \"require('node-addon-api').gyp\")"], + 'cflags!': [ '-fno-exceptions' ], + 'cflags_cc!': [ '-fno-exceptions' ], + 'xcode_settings': { + 'GCC_ENABLE_CPP_EXCEPTIONS': 'YES', + 'CLANG_CXX_LIBRARY': 'libc++', + 'MACOSX_DEPLOYMENT_TARGET': '10.7' + }, + 'msvs_settings': { + 'VCCLCompilerTool': { 'ExceptionHandling': 1 }, + } + } + ] +}
src/5-async-work/napi-asyncworker-example/node-addon-api/package.json
@@ -0,0 +1,23 @@+{+ "name": "napi-asyncworker-example",+ "description": "An example project showing how to use the Node.js Node-API AsyncWorker class",+ "version": "1.0.0",+ "main": "test/Test.js",+ "dependencies": {+ "node-addon-api": "^1.1.0"+ },+ "scripts": {+ "test": "node --napi-modules ./test/Test.js"+ },+ "gypfile": true,+ "keywords": [+ "node",+ "napi",+ "n-api",+ "async",+ "asyncworker",+ "example"+ ],+ "author": "Jim Schlight",+ "license": "MIT"+}
src/5-async-work/napi-asyncworker-example/node-addon-api/src/RunSimpleAsyncWorker.cc
@@ -0,0 +1,19 @@+#include "SimpleAsyncWorker.h" + +Value runSimpleAsyncWorker(const CallbackInfo& info) { + int runTime = info[0].As<Number>(); + Function callback = info[1].As<Function>(); + SimpleAsyncWorker* asyncWorker = new SimpleAsyncWorker(callback, runTime); + asyncWorker->Queue(); + std::string msg = + "SimpleAsyncWorker for " + std::to_string(runTime) + " seconds queued."; + return String::New(info.Env(), msg.c_str()); +}; + +Object Init(Env env, Object exports) { + exports["runSimpleAsyncWorker"] = Function::New( + env, runSimpleAsyncWorker, std::string("runSimpleAsyncWorker")); + return exports; +} + +NODE_API_MODULE(addon, Init)
src/5-async-work/napi-asyncworker-example/node-addon-api/src/SimpleAsyncWorker.cc
@@ -0,0 +1,17 @@+#include "SimpleAsyncWorker.h" +#include <chrono> +#include <thread> + +SimpleAsyncWorker::SimpleAsyncWorker(Function& callback, int runTime) + : AsyncWorker(callback), runTime(runTime){}; + +void SimpleAsyncWorker::Execute() { + std::this_thread::sleep_for(std::chrono::seconds(runTime)); + if (runTime == 4) SetError("Oops! Failed after 'working' 4 seconds."); +}; + +void SimpleAsyncWorker::OnOK() { + std::string msg = "SimpleAsyncWorker returning after 'working' " + + std::to_string(runTime) + " seconds."; + Callback().Call({Env().Null(), String::New(Env(), msg)}); +};
src/5-async-work/napi-asyncworker-example/node-addon-api/src/SimpleAsyncWorker.h
@@ -0,0 +1,15 @@+#pragma once +#include <napi.h> +using namespace Napi; + +class SimpleAsyncWorker : public AsyncWorker { + public: + SimpleAsyncWorker(Function& callback, int runTime); + virtual ~SimpleAsyncWorker(){}; + + void Execute(); + void OnOK(); + + private: + int runTime; +};
src/5-async-work/napi-asyncworker-example/node-addon-api/test/Test.js
@@ -0,0 +1,18 @@+const runWorker = require('../build/Release/napi-asyncworker-example-native'); + +let result = runWorker.runSimpleAsyncWorker(2, AsyncWorkerCompletion); +console.log("runSimpleAsyncWorker returned '"+result+"'."); + +result = runWorker.runSimpleAsyncWorker(4, AsyncWorkerCompletion); +console.log("runSimpleAsyncWorker returned '"+result+"'."); + +result = runWorker.runSimpleAsyncWorker(8, AsyncWorkerCompletion); +console.log("runSimpleAsyncWorker returned '"+result+"'."); + +function AsyncWorkerCompletion (err, result) { + if (err) { + console.log("SimpleAsyncWorker returned an error: ", err); + } else { + console.log("SimpleAsyncWorker returned '"+result+"'."); + } +};
src/6-threadsafe-function/promise-callback-demo/node-addon-api/README.md
@@ -0,0 +1,7 @@+# Node-API Promise Callback Demo++To build and run this program on your system, clone it to your computer and run these two commands inside your clone:++```+npm install+```
src/6-threadsafe-function/promise-callback-demo/node-addon-api/binding.gyp
@@ -0,0 +1,20 @@+{+ 'targets': [+ {+ 'target_name': 'promise-callback-demo',+ 'sources': [ 'src/promise_callback_demo.cc' ],+ 'include_dirs': ["<!@(node -p \"require('node-addon-api').include\")"],+ 'dependencies': ["<!(node -p \"require('node-addon-api').gyp\")"],+ 'cflags!': [ '-fno-exceptions' ],+ 'cflags_cc!': [ '-fno-exceptions' ],+ 'xcode_settings': {+ 'GCC_ENABLE_CPP_EXCEPTIONS': 'YES',+ 'CLANG_CXX_LIBRARY': 'libc++',+ 'MACOSX_DEPLOYMENT_TARGET': '10.7'+ },+ 'msvs_settings': {+ 'VCCLCompilerTool': { 'ExceptionHandling': 1 },+ }+ }+ ]+}
src/6-threadsafe-function/promise-callback-demo/node-addon-api/package.json
@@ -0,0 +1,18 @@+{+ "name": "promise-callback-demo",+ "main": "test/index.js",+ "private": true,+ "dependencies": {+ "node-addon-api": "^4.0.0"+ },+ "scripts": {+ "install": "node-gyp rebuild && node test"+ },+ "engines": {+ "node": ">= 10.16.0"+ },+ "version": "1.0.0",+ "description": "An promise callback demo",+ "author": "Node-API Team",+ "license": "ISC"+}
src/6-threadsafe-function/promise-callback-demo/node-addon-api/src/promise_callback_demo.cc
@@ -0,0 +1,121 @@+#include <napi.h>+#include <future>+#include <iostream>+#include <thread>++using namespace Napi;++class PromiseCallbackDemo;+using Context = PromiseCallbackDemo;+using DataType = std::promise<std::string>;+void CallJs(Napi::Env env, Function callback, Context* context, DataType* data);+using TSFN = TypedThreadSafeFunction<Context, DataType, CallJs>;+using FinalizerDataType = void;++class PromiseCallbackDemo : public Napi::ObjectWrap<PromiseCallbackDemo> {+ public:+ PromiseCallbackDemo(const Napi::CallbackInfo&);++ static Napi::Function GetClass(Napi::Env);++ std::thread native_thread;+ TSFN tsfn;+};++void thread_entry(PromiseCallbackDemo* obj) {+ for (auto i = 0; i < 10; ++i) {+ std::promise<std::string> promise;+ auto future = promise.get_future();+ obj->tsfn.NonBlockingCall(&promise);++ // With exceptions disabled, STL calls will abort the thread, since we+ // cannot try/catch, as the calls to `std::make_exception_ptr()` will abort.+ try {+ auto result = future.get();+ std::cout << "Result from JS, call " << i << ": " << result << "\n";+ } catch (const std::exception& e) {+ std::cout << "Error from JS, call " << i << ": " << e.what() << "\n";+ }+ }+ obj->tsfn.Release();+}++void CallJs(Napi::Env env,+ Function callback,+ Context* context,+ DataType* data) {+ // Is the JavaScript environment still available to call into, eg. the TSFN is+ // not aborted+ if (env != nullptr) {+ auto jsPromise = callback.Call({}).As<Napi::Object>();+ auto then = jsPromise.Get("then").As<Function>();+ // Attach the resolve handler.+ // TODO proper error handling (add failure handler)+ then.Call(jsPromise,+ {Napi::Function::New(env,+ [=](const CallbackInfo& info) {+ auto result =+ info[0].As<String>().Utf8Value();+ data->set_value(result);+ }),+ Napi::Function::New(env, [=](const CallbackInfo& info) {+ auto result = info[0].As<String>().Utf8Value();+ data->set_exception(std::make_exception_ptr(+ std::runtime_error("Error in jsCallback")));+ })});++ } else {+ data->set_exception(std::make_exception_ptr(+ std::runtime_error("Environment is shut down")));+ }+}++PromiseCallbackDemo::PromiseCallbackDemo(const Napi::CallbackInfo& info)+ : ObjectWrap(info) {+ Napi::Env env = info.Env();++ // Argument checking+ if (info.Length() < 1) {+ Napi::TypeError::New(env, "Wrong number of arguments")+ .ThrowAsJavaScriptException();+ return;+ }++ if (!info[0].IsFunction()) {+ Napi::TypeError::New(env, "First argument must be a function")+ .ThrowAsJavaScriptException();+ return;+ }++ // Since we are storing the std::thread on this ObjectWrap instance, we should+ // reference it so it doesn't get garbage collected, and unreference it on+ // TSFN finalize.+ Ref();++ tsfn = TSFN::New(+ env,+ info[0].As<Function>(), // JavaScript function called asynchronously+ "PromiseCallbackDemo", // Name+ 0, // Unlimited queue+ 1, // Only one thread will use this initially+ this, // Context+ [](Napi::Env,+ FinalizerDataType*,+ Context* ctx) { // Finalizer used to clean threads up+ ctx->Unref();+ ctx->native_thread.join();+ });++ native_thread = std::thread(thread_entry, this);+}++Napi::Function PromiseCallbackDemo::GetClass(Napi::Env env) {+ return DefineClass(env, "PromiseCallbackDemo", {});+}++Napi::Object Init(Napi::Env env, Napi::Object exports) {+ exports["PromiseCallbackDemo"] = PromiseCallbackDemo::GetClass(env);+ return exports;+}++NODE_API_MODULE(addon, Init)
src/6-threadsafe-function/promise-callback-demo/node-addon-api/test/index.js
@@ -0,0 +1,11 @@+const { PromiseCallbackDemo } = require('../build/Release/promise-callback-demo');++let shouldFail = false;+// Resolve or reject a promise with an ISO date format after 100 milliseconds+function jsCallback() {+ return new Promise((resolve, reject) => setTimeout(() => {+ ((shouldFail = !shouldFail) ? reject : resolve)(new Date().toISOString());+ }, 100));+}++new PromiseCallbackDemo(jsCallback);
src/6-threadsafe-function/thread_safe_function_counting/node-addon-api/addon.cc
@@ -0,0 +1,106 @@+#include <chrono>+#include <thread>+#include "napi.h"++constexpr size_t ARRAY_LENGTH = 10;++// Data structure representing our thread-safe function context.+struct TsfnContext {+ TsfnContext(Napi::Env env) : deferred(Napi::Promise::Deferred::New(env)) {+ for (size_t i = 0; i < ARRAY_LENGTH; ++i) ints[i] = i;+ };++ // Native Promise returned to JavaScript+ Napi::Promise::Deferred deferred;++ // Native thread+ std::thread nativeThread;++ // Some data to pass around+ int ints[ARRAY_LENGTH];++ Napi::ThreadSafeFunction tsfn;+};++// The thread entry point. This takes as its arguments the specific+// threadsafe-function context created inside the main thread.+void threadEntry(TsfnContext* context);++// The thread-safe function finalizer callback. This callback executes+// at destruction of thread-safe function, taking as arguments the finalizer+// data and threadsafe-function context.+void FinalizerCallback(Napi::Env env, void* finalizeData, TsfnContext* context);++// Exported JavaScript function. Creates the thread-safe function and native+// thread. Promise is resolved in the thread-safe function's finalizer.+Napi::Value CreateTSFN(const Napi::CallbackInfo& info) {+ Napi::Env env = info.Env();++ // Construct context data+ auto testData = new TsfnContext(env);++ // Create a new ThreadSafeFunction.+ testData->tsfn = Napi::ThreadSafeFunction::New(+ env, // Environment+ info[0].As<Napi::Function>(), // JS function from caller+ "TSFN", // Resource name+ 0, // Max queue size (0 = unlimited).+ 1, // Initial thread count+ testData, // Context,+ FinalizerCallback, // Finalizer+ (void*)nullptr // Finalizer data+ );+ testData->nativeThread = std::thread(threadEntry, testData);++ // Return the deferred's Promise. This Promise is resolved in the thread-safe+ // function's finalizer callback.+ return testData->deferred.Promise();+}++// The thread entry point. This takes as its arguments the specific+// threadsafe-function context created inside the main thread.+void threadEntry(TsfnContext* context) {+ // This callback transforms the native addon data (int *data) to JavaScript+ // values. It also receives the treadsafe-function's registered callback, and+ // may choose to call it.+ auto callback = [](Napi::Env env, Napi::Function jsCallback, int* data) {+ jsCallback.Call({Napi::Number::New(env, *data)});+ };++ for (size_t index = 0; index < ARRAY_LENGTH; ++index) {+ // Perform a call into JavaScript.+ napi_status status =+ context->tsfn.BlockingCall(&context->ints[index], callback);++ if (status != napi_ok) {+ Napi::Error::Fatal(+ "ThreadEntry",+ "Napi::ThreadSafeNapi::Function.BlockingCall() failed");+ }+ // Sleep for some time.+ std::this_thread::sleep_for(std::chrono::milliseconds(200));+ }++ // Release the thread-safe function. This decrements the internal thread+ // count, and will perform finalization since the count will reach 0.+ context->tsfn.Release();+}++void FinalizerCallback(Napi::Env env,+ void* finalizeData,+ TsfnContext* context) {+ // Join the thread+ context->nativeThread.join();++ // Resolve the Promise previously returned to JS via the CreateTSFN method.+ context->deferred.Resolve(Napi::Boolean::New(env, true));+ delete context;+}++// Addon entry point+Napi::Object Init(Napi::Env env, Napi::Object exports) {+ exports["createTSFN"] = Napi::Function::New(env, CreateTSFN);+ return exports;+}++NODE_API_MODULE(addon, Init)
src/6-threadsafe-function/thread_safe_function_counting/node-addon-api/addon.js
@@ -0,0 +1,9 @@+const { createTSFN } = require('bindings')('addon');++const callback = (...args) => { + console.log(new Date, ...args); +};++void async function() {+ console.log(await createTSFN(callback));+}();
src/6-threadsafe-function/thread_safe_function_counting/node-addon-api/binding.gyp
@@ -0,0 +1,21 @@+{+ 'targets': [{+ 'target_name': 'addon',+ 'defines': ['V8_DEPRECATION_WARNINGS=1'],+ 'sources': ['addon.cc'],+ 'include_dirs': ["<!@(node -p \"require('node-addon-api').include\")"],+ 'dependencies': ["<!(node -p \"require('node-addon-api').gyp\")"],+ 'cflags!': ['-fno-exceptions'],+ 'cflags_cc!': ['-fno-exceptions'],+ 'xcode_settings': {+ 'GCC_ENABLE_CPP_EXCEPTIONS': 'YES',+ 'CLANG_CXX_LIBRARY': 'libc++',+ 'MACOSX_DEPLOYMENT_TARGET': '10.7',+ },+ 'msvs_settings': {+ 'VCCLCompilerTool': {+ 'ExceptionHandling': 1+ },+ },+ }]+}
src/6-threadsafe-function/thread_safe_function_counting/node-addon-api/package.json
@@ -0,0 +1,15 @@+{+ "name": "tsfn-counting",+ "version": "0.0.0",+ "description": "Thread-safe Function Example",+ "main": "addon.js",+ "private": true,+ "gypfile": true,+ "dependencies": {+ "node-addon-api": "*",+ "bindings": "*"+ },+ "engines": {+ "node": ">= 10.16.0"+ }+}
src/6-threadsafe-function/thread_safe_function_round_trip/napi/binding.gyp
@@ -0,0 +1,8 @@+{+ 'targets': [+ {+ 'target_name': 'round_trip',+ 'sources': [ 'round_trip.c' ]+ }+ ]+}
src/6-threadsafe-function/thread_safe_function_round_trip/napi/index.js
@@ -0,0 +1,14 @@+const bindings = require('bindings')('round_trip');++bindings.startThread(item => {+ const thePrime = item.prime;+ console.log('The prime: ' + thePrime);++ // Answer the call with a 90% probability of returning true somewhere between+ // 200 and 400 ms from now.+ setTimeout(() => {+ const theAnswer = (Math.random() > 0.1);+ console.log(thePrime + ': answering with ' + theAnswer);+ bindings.registerReturnValue(item, theAnswer);+ }, Math.random() * 200 + 200);+});
src/6-threadsafe-function/thread_safe_function_round_trip/napi/package.json
@@ -0,0 +1,17 @@+{+ "name": "thread_safe_function_round_trip",+ "version": "0.0.0",+ "description": "Thread-safe Function Example With JavaScript Round Trip",+ "main": "index.js",+ "private": true,+ "dependencies": {+ "bindings": "~1.2.1"+ },+ "engines": {+ "node": ">= 10.6.0"+ },+ "scripts": {+ "test": "node index.js"+ },+ "gypfile": true+}
src/6-threadsafe-function/thread_safe_function_round_trip/napi/round_trip.c
@@ -0,0 +1,427 @@+#include <stdio.h>+#include <stdlib.h>+#include <string.h>+#include <uv.h>+#define NAPI_EXPERIMENTAL+#include <node_api.h>++#define CHECK(expr) \+ { \+ if ((expr) == 0) { \+ fprintf(stderr, "%s:%d: failed assertion `%s'\n", __FILE__, __LINE__, #expr); \+ fflush(stderr); \+ abort(); \+ } \+ }++#define REPORT_EVERY 1000++// An item that will be generated from the thread, passed into JavaScript, and+// ultimately marked as resolved when the JavaScript passes it back into the+// addon instance with a return value.+typedef struct ThreadItem {+ // This field is read-only once set, so it need not be protected by the mutex.+ int the_prime;++ // This field is only accessed from the secondary thread, so it also need not+ // be protected by the mutex.+ struct ThreadItem* next;++ // These two values must be protected by the mutex.+ bool call_has_returned;+ bool return_value;+} ThreadItem;++// The data associated with an instance of the addon. This takes the place of+// global static variables, while allowing multiple instances of the addon to+// co-exist.+typedef struct {+ uv_mutex_t check_status_mutex;+ uv_thread_t the_thread;+ napi_threadsafe_function tsfn;+ napi_ref thread_item_constructor;+ bool js_accepts;+} AddonData;++// This function is responsible for converting the native data coming in from+// the secondary thread to JavaScript values, and for calling the JavaScript+// function. It may also be called with `env` and `js_cb` set to `NULL` when+// Node.js is terminating and there are items coming in from the secondary+// thread left to process. In that case, this function does nothing, since it is+// the secondary thread that frees the items.+static void CallJs(napi_env env, napi_value js_cb, void* context, void* data) {+ AddonData* addon_data = (AddonData*)context;+ napi_value constructor;++ // The semantics of this example are such that, once the JavaScript returns+ // `false`, the `ThreadItem` structures can no longer be accessed, because the+ // thread terminates and frees them all. Thus, we record the instant when+ // JavaScript returns `false` by setting `addon_data->js_accepts` to `false`+ // in `RegisterReturnValue` below, and we use the value here to decide whether+ // the data coming in from the secondary thread is stale or not.+ if (addon_data->js_accepts && !(env == NULL || js_cb == NULL)) {+ napi_value undefined, js_thread_item;+ // Retrieve the JavaScript `undefined` value. This will serve as the `this`+ // value for the function call.+ CHECK(napi_get_undefined(env, &undefined) == napi_ok);++ // Retrieve the constructor for the JavaScript class from which the item+ // holding the native data will be constructed.+ CHECK(napi_get_reference_value(env,+ addon_data->thread_item_constructor,+ &constructor) == napi_ok);++ // Construct a new instance of the JavaScript class to hold the native item.+ CHECK(napi_new_instance(env,+ constructor,+ 0,+ NULL,+ &js_thread_item) == napi_ok);++ // Associate the native item with the newly constructed JavaScript object.+ // We assume that the JavaScript side will eventually pass this JavaScript+ // object back to us via `RegisterReturnValue`, which will allow the+ // eventual deallocation of the native data. That's why we do not provide a+ // finalizer here.+ CHECK(napi_wrap(env, js_thread_item, data, NULL, NULL, NULL) == napi_ok);++ // Call the JavaScript function with the item as wrapped into an instance of+ // the JavaScript `ThreadItem` class and the prime.+ CHECK(napi_call_function(env,+ undefined,+ js_cb,+ 1,+ &js_thread_item,+ NULL) == napi_ok);+ }+}++// When the thread is finished we join it to prevent memory leaks. We can safely+// set `addon_data->tsfn` to NULL, because the thread-safe function will be+// cleaned up in the background in response to the secondary thread having+// called `napi_release_threadsafe_function()`.+static void ThreadFinished(napi_env env, void* data, void* context) {+ (void) context;+ AddonData* addon_data = (AddonData*)data;+ CHECK(uv_thread_join(&(addon_data->the_thread)) == 0);+ addon_data->tsfn = NULL;+}++// The secondary thread produces prime numbers using a very inefficient+// algorithm and calls into JavaScript with every REPORT_EVERYth prime number.+// After each call it checks whether any of the previous calls have produced a+// return value, and, if so, whether that return value is `false`. A `false`+// return value indicates that the JavaScript side is no longer interested in+// receiving any values and that the thread and the thread-safe function are to+// be cleaned up. On the JavaScript thread, this is marked in+// `addon_data->js_accepts`. When set to `false`, the JavaScript thread will not+// access the thread items any further, so they can be safely deleted on this+// thread.+static void PrimeThread(void* data) {+ AddonData* addon_data = (AddonData*) data;+ int idx_outer, idx_inner;+ int prime_count = 0;+ ThreadItem* first = NULL;+ ThreadItem* current = NULL;+ ThreadItem* previous = NULL;+ ThreadItem* returned = NULL;++ // Check each integer whether it's a prime.+ for (idx_outer = 2 ;; idx_outer++) {++ // Check whether `idx_outer` is divisible by anything up to and not+ // including itself.+ for (idx_inner = 2;+ idx_inner < idx_outer && idx_outer % idx_inner != 0;+ idx_inner++);++ // If we find a prime, and it is REPORT_EVERY primes away from the previous+ // prime we found, then we send it to JavaScript.+ if (idx_inner >= idx_outer && (++prime_count % REPORT_EVERY) == 0) {+ // Create a new thread item and attach it to the list of outstanding+ // `ThreadItem` structures representing calls into JavaScript for which+ // no return value has yet been established.+ current = memset(malloc(sizeof(*current)), 0, sizeof(*current));+ current->the_prime = idx_outer;+ current->call_has_returned = false;+ current->return_value = false;+ current->next = first;+ first = current;++ // Pass the new item into JavaScript.+ CHECK(napi_call_threadsafe_function(addon_data->tsfn,+ first,+ napi_tsfn_blocking) == napi_ok);+ }++ // Pass over all outstanding thread items and check whether any of them have+ // returned.+ for (current = first, previous = NULL, returned = NULL;+ current != NULL && returned == NULL;+ previous = current,+ current = current->next) {+ uv_mutex_lock(&(addon_data->check_status_mutex));+ if (current->call_has_returned) {+ // Unhook the call that has returned from the list.+ if (previous != NULL) {+ previous->next = current->next;+ } else {+ first = current->next;+ }+ returned = current;+ }+ uv_mutex_unlock(&(addon_data->check_status_mutex));+ }++ // Process a return value. Free the `ThreadItem` that returned it, and break+ // out of the loop if the return value was `false`.+ if (returned != NULL) {+ // Save the return value to a local variable because we have to check it+ // after having freed the structure wherein it is stored.+ bool return_value = returned->return_value;+ free(returned);+ if (!return_value) {+ break;+ }+ }+ }++ // Before terminating the thread we free the remaining queue items. `CallJs`+ // will be called with pointers to these items, perhaps after this thread has+ // already freed them, but that's OK, because on the JavaScript thread+ // `addon_data->js_accepts` will certainly have been set to `false`, and so+ // `CallJs` will not dereference the stale pointers.+ for (current = first; current != NULL;) {+ previous = current;+ current = current->next;+ free(previous);+ }++ // Release the thread-safe function. This causes it to be cleaned up in the+ // background.+ CHECK(napi_release_threadsafe_function(addon_data->tsfn,+ napi_tsfn_release) == napi_ok);+}++// This binding can be called from JavaScript to start the asynchronous prime+// generator.+static napi_value StartThread(napi_env env, napi_callback_info info) {+ size_t argc = 1;+ napi_value js_cb, work_name;+ AddonData* addon_data;++ // The binding accepts one parameter - the JavaScript callback function to+ // call.+ CHECK(napi_get_cb_info(env,+ info,+ &argc,+ &js_cb,+ NULL,+ (void*)&addon_data) == napi_ok);++ // We do not create a second thread if one is already running.+ CHECK(addon_data->tsfn == NULL && "Work already in progress");++ addon_data->js_accepts = true;++ // This string describes the asynchronous work.+ CHECK(napi_create_string_utf8(env,+ "Thread-safe Function Round Trip Example",+ NAPI_AUTO_LENGTH,+ &work_name) == napi_ok);++ // The thread-safe function will be created with an unlimited queue and with+ // an initial thread count of 1. The secondary thread will release the+ // thread-safe function, decreasing its thread count to 0, thereby setting off+ // the process of cleaning up the thread-safe function.+ CHECK(napi_create_threadsafe_function(env,+ js_cb,+ NULL,+ work_name,+ 0,+ 1,+ addon_data,+ ThreadFinished,+ addon_data,+ CallJs,+ &addon_data->tsfn) == napi_ok);++ // Create the thread that will produce primes and that will call into+ // JavaScript using the thread-safe function.+ CHECK(uv_thread_create(&(addon_data->the_thread), PrimeThread, addon_data) == 0);++ return NULL;+}++static bool+is_thread_item (napi_env env, napi_ref constructor_ref, napi_value value) {+ bool validate;+ napi_value constructor;+ CHECK(napi_get_reference_value(env,+ constructor_ref,+ &constructor) == napi_ok);+ CHECK(napi_instanceof(env, value, constructor, &validate) == napi_ok);+ return validate;+}++// We use a separate binding to register a return value for a given call into+// JavaScript, represented by a `ThreadItem` object on both the JavaScript side+// and the native side. This allows the JavaScript side to asynchronously+// determine the return value.+static napi_value RegisterReturnValue(napi_env env, napi_callback_info info) {+ // This function accepts two parameters:+ // 1. The thread item passed into JavaScript via `CallJs`, and+ // 2. The desired return value.+ size_t argc = 2;+ napi_value argv[2];+ AddonData* addon_data;+ bool return_value;+ ThreadItem* item;++ // Retrieve the parameters with which this function was called.+ CHECK(napi_get_cb_info(env,+ info,+ &argc,+ argv,+ NULL,+ (void*)&addon_data) == napi_ok);++ // If this function recorded a return value of `false` before, it means that+ // the thread and the associated thread-safe function are shutting down. This,+ // in turn means that the wrapped `ThreadItem` that was received in the first+ // argument may also be stale. Our best strategy is to do nothing and return+ // to JavaScript.+ if (!addon_data->js_accepts) {+ return NULL;+ }++ CHECK(argc == 2 && "Exactly two arguments were received");++ // Make sure the first parameter is an instance of the `ThreadItem` class.+ // This type check ensures that there *is* a pointer stored inside the+ // JavaScript object, and that the pointer is to a `ThreadItem` structure.+ CHECK(is_thread_item(env, addon_data->thread_item_constructor, argv[0]));++ // Retrieve the native data from the item.+ CHECK(napi_unwrap(env, argv[0], (void**)&item) == napi_ok);++ // Retrieve the desired return value.+ CHECK(napi_get_value_bool(env, argv[1], &return_value) == napi_ok);++ // Set `js_accepts` to false in case the JavaScript callback returned false.+ if (addon_data->js_accepts) {+ addon_data->js_accepts = return_value;+ }++ // Mark the thread item as resolved, and record the JavaScript return value.+ uv_mutex_lock(&(addon_data->check_status_mutex));+ item->call_has_returned = true;+ item->return_value = return_value;+ uv_mutex_unlock(&(addon_data->check_status_mutex));++ return NULL;+}++// Constructor for instances of the `ThreadItem` class. This doesn't need to do+// anything since all we want the class for is to be able to type-check+// JavaScript objects that carry within them a pointer to a native `ThreadItem`+// structure.+static napi_value ThreadItemConstructor(napi_env env, napi_callback_info info) {+ return NULL;+}++// Getter for the `prime` property of the `ThreadItem` class.+static napi_value GetPrime(napi_env env, napi_callback_info info) {+ napi_value jsthis, prime_property;+ AddonData* ad;+ CHECK(napi_ok == napi_get_cb_info(env, info, 0, 0, &jsthis, (void*)&ad));+ CHECK(is_thread_item(env, ad->thread_item_constructor, jsthis));+ ThreadItem* item;+ CHECK(napi_ok == napi_unwrap(env, jsthis, (void**)&item));+ CHECK(napi_ok == napi_create_int32(env, item->the_prime, &prime_property));+ return prime_property;+}++static void addon_is_unloading(napi_env env, void* data, void* hint) {+ AddonData* addon_data = (AddonData*)data;+ uv_mutex_destroy(&(addon_data->check_status_mutex));+ CHECK(napi_delete_reference(env,+ addon_data->thread_item_constructor) == napi_ok);+ free(data);+}++// Initialize an instance of this addon. This function may be called multiple+// times if multiple instances of Node.js are running on multiple threads, or if+// there are multiple Node.js contexts running on the same thread. The return+// value and the formal parameters in comments remind us that the function body+// that follows, within which we initialize the addon, has available to it the+// variables named in the formal parameters, and that it must return a+// `napi_value`.+/*napi_value*/ NAPI_MODULE_INIT(/*napi_env env, napi_value exports*/) {+ // Create the native data that will be associated with this instance of the+ // addon.+ AddonData* addon_data =+ memset(malloc(sizeof(*addon_data)), 0, sizeof(*addon_data));++ // Attach the addon data to the exports object to ensure that they are+ // destroyed together.+ CHECK(napi_wrap(env,+ exports,+ addon_data,+ addon_is_unloading,+ NULL,+ NULL) == napi_ok);++ // Initialize the various members of the `AddonData` associated with this+ // addon instance.+ CHECK(uv_mutex_init(&(addon_data->check_status_mutex)) == 0);++ napi_value thread_item_class;+ napi_property_descriptor thread_item_properties[] = {+ { "prime", 0, 0, GetPrime, 0, 0, napi_enumerable, addon_data }+ };+ CHECK(napi_define_class(env,+ "ThreadItem",+ NAPI_AUTO_LENGTH,+ ThreadItemConstructor,+ addon_data,+ 1,+ thread_item_properties,+ &thread_item_class) == napi_ok);+ CHECK(napi_create_reference(env,+ thread_item_class,+ 1,+ &(addon_data->thread_item_constructor)) ==+ napi_ok);++ // Expose the two bindings this addon provides.+ napi_property_descriptor export_properties[] = {+ {+ "startThread",+ NULL,+ StartThread,+ NULL,+ NULL,+ NULL,+ napi_default,+ addon_data+ },+ {+ "registerReturnValue",+ NULL,+ RegisterReturnValue,+ NULL,+ NULL,+ NULL,+ napi_default,+ addon_data+ }+ };+ CHECK(napi_define_properties(env,+ exports,+ sizeof(export_properties) /+ sizeof(export_properties[0]),+ export_properties) == napi_ok);++ return exports;+}
src/6-threadsafe-function/thread_safe_function_with_object_wrap/node-addon-api/binding.gyp
@@ -0,0 +1,17 @@+{+ "targets": [+ {+ "target_name": "tsfn_object_wrap",+ "sources": [+ "tsfn_object_wrap.cc",+ ],+ "defines": [+ "NAPI_DISABLE_CPP_EXCEPTIONS",+ "NODE_API_SWALLOW_UNTHROWABLE_EXCEPTIONS"+ ],+ "include_dirs": [+ "<!@(node -p \"require('node-addon-api').include\")"+ ],+ }+ ]+}
src/6-threadsafe-function/thread_safe_function_with_object_wrap/node-addon-api/package.json
@@ -0,0 +1,11 @@+{+ "name": "tsfn-test",+ "version": "1.0.0",+ "description": "",+ "main": "index.js",+ "author": "",+ "dependencies": {+ "bindings": "*",+ "node-addon-api": "^7.0.0"+ }+}
src/6-threadsafe-function/thread_safe_function_with_object_wrap/node-addon-api/tsfn_object_wrap.cc
@@ -0,0 +1,77 @@+#include <inttypes.h>+#include <napi.h>+#include <thread>++// A secondary thread increments a value starting from 0 and calls a JS callback+// on the JavaScript main thread.++// The ObjectWrap subclass whose instances own such a secondary thread.+class TsfnObjectWrap : public Napi::ObjectWrap<TsfnObjectWrap> {+ public:+ static Napi::Object Init(Napi::Env env) {+ return DefineClass(env, "TsfnObjectWrap", {});+ }++ TsfnObjectWrap(const Napi::CallbackInfo& info)+ : Napi::ObjectWrap<TsfnObjectWrap>(info) {+ // Whenever we construct a new instance of type TsfnObjectWrap, we construct+ // a thread-safe function that can be called from a secondary thread and+ // which provides a value to the JavaScript main thread.+ _tsfn = Napi::ThreadSafeFunction::New(+ info.Env(), info[0].As<Napi::Function>(), "TsfnObjectWrap", 1, 2);++ _thread = std::thread(&TsfnObjectWrap::Thread, std::ref(_tsfn));+ }++ ~TsfnObjectWrap() {+ _tsfn.Abort();+ _thread.join();+ }++ private:+ // This is the secondary thread.+ static void Thread(const Napi::ThreadSafeFunction& tsfn) {+ int64_t the_value = 0;+ int64_t buffer[3] = {0, 0, 0};+ int idx = 0;++ // Since we're calling the JavaScript main thread in a blocking fashion,+ // a buffer of three values is sufficient for synchronizing with the main+ // thread without losing a single value and without having to allocate an+ // integer on the heap for each call into JavaScript.+ while (true) {+ buffer[idx] = the_value;+ idx = (idx + 1) % 3;+ int64_t* value_ref = &buffer[idx];+ napi_status status = tsfn.BlockingCall(+ value_ref, [](Napi::Env env, Napi::Function fn, int64_t* data) {+ int64_t native_value = *data;+ Napi::Value result =+ fn.Call({Napi::Number::New(env, native_value)});+ if (result.IsEmpty()) {+ printf("main with %" PRId64 ": result was empty!\n",+ native_value);+ } else {+ printf("main with %" PRId64 ": Done!\n",+ result.As<Napi::Number>().Int64Value());+ }+ });+ // We break out of the infinite loop when we're informed that the thread-+ // safe function is being torn down.+ if (status == napi_closing) {+ break;+ }+ the_value++;+ }+ }++ Napi::ThreadSafeFunction _tsfn;+ std::thread _thread;+};++// Boilerplate code to define an add-on that consists of the above class.+Napi::Object TsfnObjectWrapExampleInit(Napi::Env env, Napi::Object exports) {+ return TsfnObjectWrap::Init(env);+}++NODE_API_MODULE(TsfnObjectWrapExample, TsfnObjectWrapExampleInit)
src/6-threadsafe-function/thread_safe_function_with_object_wrap/node-addon-api/tsfn_object_wrap.js
@@ -0,0 +1,18 @@+const TsfnObjectWrap = require('bindings')('tsfn_object_wrap.node')+const x = new TsfnObjectWrap((value) => {+ // Do something, anything, with x to keep it in scope, otherwise the instance+ // will be collected and the process will exit.+ x.someProperty = value;++ console.log('JS1: Called with ' + value);+ return -value;+});++const y = new TsfnObjectWrap((value) => {+ // Do something, anything, with y to keep it in scope, otherwise the instance+ // will be collected and the process will exit.+ y.someProperty = value;++ console.log('JS2: Called with ' + value);+ return -value;+});
src/6-threadsafe-function/threadsafe-async-iterator/node-addon-api/CMakeLists.txt
@@ -0,0 +1,23 @@+project (example)+include_directories(${CMAKE_JS_INC} node_modules/node-addon-api/)+cmake_minimum_required(VERSION 3.18)++set(CMAKE_CXX_STANDARD 20)+set(CMAKE_CXX_STANDARD_REQUIRED ON)+include_directories(${CMAKE_JS_INC})+file(GLOB SOURCE_FILES "*.cc")+add_library(${PROJECT_NAME} SHARED ${SOURCE_FILES} ${CMAKE_JS_SRC})+set_target_properties(${PROJECT_NAME} PROPERTIES PREFIX "" SUFFIX ".node")+target_link_libraries(${PROJECT_NAME} ${CMAKE_JS_LIB})++# Include Node-API wrappers+execute_process(COMMAND node -p "require('node-addon-api').include"+ WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}+ OUTPUT_VARIABLE NODE_ADDON_API_DIR+ )+string(REGEX REPLACE "[\r\n\"]" "" NODE_ADDON_API_DIR ${NODE_ADDON_API_DIR})++target_include_directories(${PROJECT_NAME} PRIVATE ${NODE_ADDON_API_DIR})++# define NAPI_VERSION+add_definitions(-DNAPI_VERSION=6)
src/6-threadsafe-function/threadsafe-async-iterator/node-addon-api/example.cc
@@ -0,0 +1,163 @@+#include <napi.h>+#include <condition_variable>+#include <functional>+#include <memory>+#include <mutex>+#include <optional>+#include <thread>++using namespace Napi;++class ThreadSafeAsyncIteratorExample+ : public ObjectWrap<ThreadSafeAsyncIteratorExample> {+ public:+ ThreadSafeAsyncIteratorExample(const CallbackInfo& info)+ : ObjectWrap<ThreadSafeAsyncIteratorExample>(info),+ _current(info[0].As<Number>()),+ _last(info[1].As<Number>()) {}++ static Object Init(Napi::Env env, Napi::Object exports);++ Napi::Value Iterator(const CallbackInfo& info);++ private:+ using Context = ThreadSafeAsyncIteratorExample;++ struct DataType {+ std::unique_ptr<Promise::Deferred> deferred;+ bool done;+ std::optional<int> value;+ };++ static void CallJs(Napi::Env env,+ Function callback,+ Context* context,+ DataType* data);++ using TSFN = TypedThreadSafeFunction<Context, DataType, CallJs>;++ using FinalizerDataType = void;++ int _current;+ int _last;+ TSFN _tsfn;+ std::thread _thread;+ std::unique_ptr<Promise::Deferred> _deferred;++ // Thread-safety+ std::mutex _mtx;+ std::condition_variable _cv;++ void threadEntry();++ static void FinalizerCallback(Napi::Env env,+ void*,+ ThreadSafeAsyncIteratorExample* context);+};++Object ThreadSafeAsyncIteratorExample::Init(Napi::Env env,+ Napi::Object exports) {+ Napi::Function func =+ DefineClass(env,+ "ThreadSafeAsyncIteratorExample",+ {InstanceMethod(Napi::Symbol::WellKnown(env, "asyncIterator"),+ &ThreadSafeAsyncIteratorExample::Iterator)});++ exports.Set("ThreadSafeAsyncIteratorExample", func);+ return exports;+}++Napi::Value ThreadSafeAsyncIteratorExample::Iterator(const CallbackInfo& info) {+ auto env = info.Env();++ if (_thread.joinable()) {+ Napi::Error::New(env, "Concurrent iterations not implemented.")+ .ThrowAsJavaScriptException();+ return Napi::Value();+ }++ _tsfn =+ TSFN::New(info.Env(),+ "tsfn",+ 0,+ 1,+ this,+ std::function<decltype(FinalizerCallback)>(FinalizerCallback));++ // To prevent premature garbage collection; Unref in TFSN finalizer+ Ref();++ // Create thread+ _thread = std::thread(&ThreadSafeAsyncIteratorExample::threadEntry, this);++ // Create iterable+ auto iterable = Napi::Object::New(env);++ iterable["next"] =+ Function::New(env, [this](const CallbackInfo& info) -> Napi::Value {+ std::lock_guard<std::mutex> lk(_mtx);+ auto env = info.Env();+ if (_deferred) {+ Napi::Error::New(env, "Concurrent iterations not implemented.")+ .ThrowAsJavaScriptException();+ return Napi::Value();+ }+ _deferred = std::make_unique<Promise::Deferred>(env);+ _cv.notify_all();+ return _deferred->Promise();+ });++ return iterable;+}++void ThreadSafeAsyncIteratorExample::threadEntry() {+ while (true) {+ std::unique_lock<std::mutex> lk(_mtx);+ _cv.wait(lk, [this] { return this->_deferred != nullptr; });+ auto done = _current > _last;+ if (done) {+ _tsfn.BlockingCall(new DataType{std::move(this->_deferred), true, {}});+ break;+ } else {+ std::this_thread::sleep_for(+ std::chrono::seconds(1)); // Simulate CPU-intensive work+ _tsfn.BlockingCall(+ new DataType{std::move(this->_deferred), false, _current++});+ }+ }+ _tsfn.Release();+}++void ThreadSafeAsyncIteratorExample::CallJs(Napi::Env env,+ Function callback,+ Context* context,+ DataType* data) {+ if (env != nullptr) {+ auto value = Object::New(env);++ if (data->done) {+ value["done"] = Boolean::New(env, true);+ } else {+ value["done"] = Boolean::New(env, false);+ value["value"] = Number::New(env, data->value.value());+ }+ data->deferred->Resolve(value);+ }++ if (data != nullptr) {+ delete data;+ }+}++void ThreadSafeAsyncIteratorExample::FinalizerCallback(+ Napi::Env env, void*, ThreadSafeAsyncIteratorExample* context) {+ context->_thread.join();+ context->Unref();+}++Napi::Object Init(Napi::Env env, Object exports) {+ ThreadSafeAsyncIteratorExample::Init(env, exports);+ return exports;+}++NODE_API_MODULE(example, Init)
src/6-threadsafe-function/threadsafe-async-iterator/node-addon-api/index.js
@@ -0,0 +1,14 @@+const { ThreadSafeAsyncIteratorExample } = require('bindings')('example');++async function main(from, to) {+ const iterator = new ThreadSafeAsyncIteratorExample(from, to);+ for await (const value of iterator) {+ console.log(value);+ }+}++main(0, 5)+ .catch(e => {+ console.error(e);+ process.exit(1);+ });
src/6-threadsafe-function/threadsafe-async-iterator/node-addon-api/package.json
@@ -0,0 +1,16 @@+{+ "name": "threadsafe-async-iterator-example",+ "version": "0.0.0",+ "description": "Async iterator example with threadsafe functions using node-addon-api",+ "main": "index.js",+ "private": true,+ "dependencies": {+ "bindings": "^1.5.0",+ "cmake-js": "^6.3.0",+ "node-addon-api": "^5.0.0"+ },+ "scripts": {+ "test": "node index.js",+ "install": "cmake-js compile"+ }+}
src/6-threadsafe-function/typed_threadsafe_function/node-addon-api/CMakeLists.txt
@@ -0,0 +1,23 @@+project (clock)+include_directories(${CMAKE_JS_INC} node_modules/node-addon-api/)+cmake_minimum_required(VERSION 3.18)++set(CMAKE_CXX_STANDARD 20)+set(CMAKE_CXX_STANDARD_REQUIRED ON)+include_directories(${CMAKE_JS_INC})+file(GLOB SOURCE_FILES "*.cc")+add_library(${PROJECT_NAME} SHARED ${SOURCE_FILES} ${CMAKE_JS_SRC})+set_target_properties(${PROJECT_NAME} PROPERTIES PREFIX "" SUFFIX ".node")+target_link_libraries(${PROJECT_NAME} ${CMAKE_JS_LIB})++# Include Node-API wrappers+execute_process(COMMAND node -p "require('node-addon-api').include"+ WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}+ OUTPUT_VARIABLE NODE_ADDON_API_DIR+ )+string(REGEX REPLACE "[\r\n\"]" "" NODE_ADDON_API_DIR ${NODE_ADDON_API_DIR})++target_include_directories(${PROJECT_NAME} PRIVATE ${NODE_ADDON_API_DIR})++# define NAPI_VERSION+add_definitions(-DNAPI_VERSION=4)
src/6-threadsafe-function/typed_threadsafe_function/node-addon-api/clock.cc
@@ -0,0 +1,97 @@+#include <napi.h>+#include <chrono>+#include <thread>++using namespace Napi;++using Context = Reference<Value>;+using DataType = int;+void CallJs(Napi::Env env, Function callback, Context* context, DataType* data);+using TSFN = TypedThreadSafeFunction<Context, DataType, CallJs>;+using FinalizerDataType = void;++std::thread nativeThread;+TSFN tsfn;++Value Start(const CallbackInfo& info) {+ Napi::Env env = info.Env();++ if (info.Length() < 2) {+ throw TypeError::New(env, "Expected two arguments");+ } else if (!info[0].IsFunction()) {+ throw TypeError::New(env, "Expected first arg to be function");+ } else if (!info[1].IsNumber()) {+ throw TypeError::New(env, "Expected second arg to be number");+ }++ int count = info[1].As<Number>().Int32Value();++ // Create a new context set to the the receiver (ie, `this`) of the function+ // call+ Context* context = new Reference<Value>(Persistent(info.This()));++ // Create a ThreadSafeFunction+ tsfn = TSFN::New(+ env,+ info[0].As<Function>(), // JavaScript function called asynchronously+ "Resource Name", // Name+ 0, // Unlimited queue+ 1, // Only one thread will use this initially+ context,+ [](Napi::Env,+ FinalizerDataType*,+ Context* ctx) { // Finalizer used to clean threads up+ nativeThread.join();+ delete ctx;+ });++ // Create a native thread+ nativeThread = std::thread([count] {+ for (int i = 0; i < count; i++) {+ // Create new data+ int* value = new int(clock());++ // Perform a blocking call+ napi_status status = tsfn.BlockingCall(value);+ if (status != napi_ok) {+ // Handle error+ break;+ }++ std::this_thread::sleep_for(std::chrono::seconds(1));+ }++ // Release the thread-safe function+ tsfn.Release();+ });++ return Boolean::New(env, true);+}++// Transform native data into JS data, passing it to the provided+// `callback` -- the TSFN's JavaScript function.+void CallJs(Napi::Env env,+ Function callback,+ Context* context,+ DataType* data) {+ // Is the JavaScript environment still available to call into, eg. the TSFN is+ // not aborted+ if (env != nullptr) {+ // On Node-API 5+, the `callback` parameter is optional; however, this+ // example does ensure a callback is provided.+ if (callback != nullptr) {+ callback.Call(context->Value(), {Number::New(env, *data)});+ }+ }+ if (data != nullptr) {+ // We're finished with the data.+ delete data;+ }+}++Napi::Object Init(Napi::Env env, Object exports) {+ exports.Set("start", Function::New(env, Start));+ return exports;+}++NODE_API_MODULE(clock, Init)
src/6-threadsafe-function/typed_threadsafe_function/node-addon-api/index.js
@@ -0,0 +1,6 @@+const { start } = require('bindings')('clock');++start.call(new Date(), function (clock) {+ const context = this;+ console.log(context, clock);+}, 5);
src/6-threadsafe-function/typed_threadsafe_function/node-addon-api/package.json
@@ -0,0 +1,17 @@+{+ "name": "example-typedthreadsafefunction",+ "version": "0.0.0",+ "description": "node-addon-api TypedThreadSafeFunction example",+ "main": "index.js",+ "private": true,+ "dependencies": {+ "bindings": "~1.2.1",+ "cmake-js": "^6.1.0",+ "node-addon-api": "^4.0.0"+ },+ "scripts": {+ "test": "node index.js",+ "install": "cmake-js compile"+ },+ "gypfile": true+}
src/7-events/emit_event_from_cpp/node-addon-api/binding.gyp
@@ -0,0 +1,30 @@+{+ "targets": [+ {+ "target_name": "emit_from_cpp",+ "sources": [+ "src/emit-from-cpp.cc"+ ],+ 'cflags!': [ '-fno-exceptions' ],+ 'cflags_cc!': [ '-fno-exceptions' ],+ 'include_dirs': ["<!@(node -p \"require('node-addon-api').include\")"],+ 'dependencies': ["<!(node -p \"require('node-addon-api').gyp\")"],+ 'conditions': [+ ['OS=="win"', {+ "msvs_settings": {+ "VCCLCompilerTool": {+ "ExceptionHandling": 1+ }+ }+ }],+ ['OS=="mac"', {+ "xcode_settings": {+ "CLANG_CXX_LIBRARY": "libc++",+ 'GCC_ENABLE_CPP_EXCEPTIONS': 'YES',+ 'MACOSX_DEPLOYMENT_TARGET': '10.7'+ }+ }]+ ]+ }+ ]+}
src/7-events/emit_event_from_cpp/node-addon-api/index.js
@@ -0,0 +1,19 @@+'use strict'++const EventEmitter = require('events').EventEmitter+const addon = require('bindings')('emit_from_cpp')++const emitter = new EventEmitter()++emitter.on('start', () => {+ console.log('### START ...')+})+emitter.on('data', (evt) => {+ console.log(evt);+})++emitter.on('end', () => {+ console.log('### END ###')+})++addon.callEmit(emitter.emit.bind(emitter))
src/7-events/emit_event_from_cpp/node-addon-api/package.json
@@ -0,0 +1,15 @@+{+ "name": "emit-event-from-cpp-example",+ "version": "0.0.0",+ "description": "Node.js Addons - emit event from C++ to JS",+ "main": "index.js",+ "private": true,+ "gypfile": true,+ "scripts": {+ "start": "node index.js"+ },+ "dependencies": {+ "node-addon-api": "*",+ "bindings": "*"+ }+}
src/7-events/emit_event_from_cpp/node-addon-api/src/emit-from-cpp.cc
@@ -0,0 +1,27 @@+#include <napi.h>++#include <chrono>+#include <iostream>+#include <thread>++Napi::Value CallEmit(const Napi::CallbackInfo& info) {+ Napi::Env env = info.Env();+ Napi::Function emit = info[0].As<Napi::Function>();+ emit.Call({Napi::String::New(env, "start")});+ for (int i = 0; i < 3; i++) {+ std::this_thread::sleep_for(std::chrono::seconds(3));+ emit.Call(+ {Napi::String::New(env, "data"), Napi::String::New(env, "data ...")});+ }+ emit.Call({Napi::String::New(env, "end")});+ return Napi::String::New(env, "OK");+}++// Init+Napi::Object Init(Napi::Env env, Napi::Object exports) {+ exports.Set(Napi::String::New(env, "callEmit"),+ Napi::Function::New(env, CallEmit));+ return exports;+}++NODE_API_MODULE(NODE_GYP_MODULE_NAME, Init);
src/7-events/inherits_from_event_emitter/node-addon-api/binding.gyp
@@ -0,0 +1,31 @@+{+ "targets": [+ {+ "target_name": "native_emitter",+ "sources": [+ "src/binding.cc",+ "src/native-emitter.cc"+ ],+ 'cflags!': [ '-fno-exceptions' ],+ 'cflags_cc!': [ '-fno-exceptions' ],+ 'include_dirs': ["<!@(node -p \"require('node-addon-api').include\")"],+ 'dependencies': ["<!(node -p \"require('node-addon-api').gyp\")"],+ 'conditions': [+ ['OS=="win"', {+ "msvs_settings": {+ "VCCLCompilerTool": {+ "ExceptionHandling": 1+ }+ }+ }],+ ['OS=="mac"', {+ "xcode_settings": {+ "CLANG_CXX_LIBRARY": "libc++",+ 'GCC_ENABLE_CPP_EXCEPTIONS': 'YES',+ 'MACOSX_DEPLOYMENT_TARGET': '10.7'+ }+ }]+ ]+ }+ ]+}
src/7-events/inherits_from_event_emitter/node-addon-api/index.js
@@ -0,0 +1,23 @@+'use strict'++const EventEmitter = require('events').EventEmitter+const NativeEmitter = require('bindings')('native_emitter').NativeEmitter+const inherits = require('util').inherits++inherits(NativeEmitter, EventEmitter)++const emitter = new NativeEmitter()++emitter.on('start', () => {+ console.log('### START ...')+})++emitter.on('data', (evt) => {+ console.log(evt)+})++emitter.on('end', () => {+ console.log('### END ###')+})++emitter.callAndEmit()
src/7-events/inherits_from_event_emitter/node-addon-api/package.json
@@ -0,0 +1,15 @@+{+ "name": "inherits-from-event-emitter-example",+ "version": "0.0.0",+ "description": "Node.js Addons - inherits from event emitter",+ "main": "index.js",+ "private": true,+ "gypfile": true,+ "scripts": {+ "start": "node index.js"+ },+ "dependencies": {+ "node-addon-api": "*",+ "bindings": "*"+ }+}
src/7-events/inherits_from_event_emitter/node-addon-api/src/binding.cc
@@ -0,0 +1,9 @@+#include <napi.h>+#include "native-emitter.h"++Napi::Object Init(Napi::Env env, Napi::Object exports) {+ NativeEmitter::Init(env, exports);+ return exports;+}++NODE_API_MODULE(NODE_GYP_MODULE_NAME, Init)
src/7-events/inherits_from_event_emitter/node-addon-api/src/native-emitter.cc
@@ -0,0 +1,40 @@+#include <chrono>+#include <iostream>+#include <thread>++#include "native-emitter.h"++Napi::FunctionReference NativeEmitter::constructor;++Napi::Object NativeEmitter::Init(Napi::Env env, Napi::Object exports) {+ Napi::Function func =+ DefineClass(env,+ "NativeEmitter",+ {InstanceMethod("callAndEmit", &NativeEmitter::CallAndEmit)});++ constructor = Napi::Persistent(func);+ constructor.SuppressDestruct();++ exports.Set("NativeEmitter", func);+ return exports;+}++NativeEmitter::NativeEmitter(const Napi::CallbackInfo& info)+ : Napi::ObjectWrap<NativeEmitter>(info) {+ // NOOP+}++Napi::Value NativeEmitter::CallAndEmit(const Napi::CallbackInfo& info) {+ Napi::Env env = info.Env();+ Napi::Function emit =+ info.This().As<Napi::Object>().Get("emit").As<Napi::Function>();+ emit.Call(info.This(), {Napi::String::New(env, "start")});+ for (int i = 0; i < 3; i++) {+ std::this_thread::sleep_for(std::chrono::seconds(1));+ emit.Call(+ info.This(),+ {Napi::String::New(env, "data"), Napi::String::New(env, "data ...")});+ }+ emit.Call(info.This(), {Napi::String::New(env, "end")});+ return Napi::String::New(env, "OK");+}
src/7-events/inherits_from_event_emitter/node-addon-api/src/native-emitter.h
@@ -0,0 +1,12 @@+#include <napi.h>++class NativeEmitter : public Napi::ObjectWrap<NativeEmitter> {+ public:+ static Napi::Object Init(Napi::Env env, Napi::Object exports);+ NativeEmitter(const Napi::CallbackInfo& info);++ private:+ static Napi::FunctionReference constructor;++ Napi::Value CallAndEmit(const Napi::CallbackInfo& info);+};
src/8-tooling/build_with_cmake/README.md
@@ -0,0 +1,33 @@+## Building Node-API Addons Using CMake.js++### Examples++The objective of these examples is to demonstrate how to build Node-API addons using [CMake.js](https://github.com/cmake-js/cmake-js#readme).++These example projects assume that CMake.js has been installed globally:++```+npm install -g cmake-js+cmake-js --help+```++Then, in each of the `napi` and `node-addon-api` directories, the following commands build and test each addon:++```+npm install+npm test+```++Complete CMake.js documentation can be found on the [CMake.js GitHub repository](https://github.com/cmake-js/cmake-js#readme).++### NAPI_VERSION++When building Node-API addons, it's important to specify to the build system the Node-API version your code is designed to work with. With CMake.js, this information is specified in the `CMakeLists.txt` file: ++```+add_definitions(-DNAPI_VERSION=3)+```++Since Node-API is ABI-stable, your Node-API addon will work, without recompilation, with the Node-API version you specify in `NAPI_VERSION` and all subsequent Node-API versions. ++In the absence of a need for features available only in a specific Node-API version, version 3 is a good choice as it is the version of Node-API that was active when Node-API left experimental status.
src/8-tooling/build_with_cmake/napi/CMakeLists.txt
@@ -0,0 +1,13 @@+cmake_minimum_required(VERSION 3.9)+cmake_policy(SET CMP0042 NEW)+set (CMAKE_CXX_STANDARD 11)++project (build-napi-with-cmake)+include_directories(${CMAKE_JS_INC})+file(GLOB SOURCE_FILES "hello.c")+add_library(${PROJECT_NAME} SHARED ${SOURCE_FILES} ${CMAKE_JS_SRC})+set_target_properties(${PROJECT_NAME} PROPERTIES PREFIX "" SUFFIX ".node")+target_link_libraries(${PROJECT_NAME} ${CMAKE_JS_LIB})++# Define NAPI_VERSION+add_definitions(-DNAPI_VERSION=3)
src/8-tooling/build_with_cmake/napi/hello.c
@@ -0,0 +1,23 @@+#include <assert.h>+#include <node_api.h>++static napi_value Method(napi_env env, napi_callback_info info) {+ napi_status status;+ napi_value world;+ status = napi_create_string_utf8(env, "Hello, world!", 13, &world);+ assert(status == napi_ok);+ return world;+}++#define DECLARE_NAPI_METHOD(name, func) \+ { name, 0, func, 0, 0, 0, napi_default, 0 }++static napi_value Init(napi_env env, napi_value exports) {+ napi_status status;+ napi_property_descriptor desc = DECLARE_NAPI_METHOD("hello", Method);+ status = napi_define_properties(env, exports, 1, &desc);+ assert(status == napi_ok);+ return exports;+}++NAPI_MODULE(hello, Init)
src/8-tooling/build_with_cmake/napi/hello.js
@@ -0,0 +1,3 @@+var addon = require('bindings')('build-napi-with-cmake');++console.log(addon.hello()); // 'world'
src/8-tooling/build_with_cmake/napi/package.json
@@ -0,0 +1,14 @@+{+ "name": "build-napi-with-cmake",+ "version": "0.0.0",+ "description": "Build Node-API native addon with CMake.",+ "main": "hello.js",+ "private": true,+ "dependencies": {+ "bindings": "~1.2.1"+ },+ "scripts": {+ "install": "cmake-js compile",+ "test": "node hello.js"+ }+}
src/8-tooling/build_with_cmake/node-addon-api/CMakeLists.txt
@@ -0,0 +1,22 @@+cmake_minimum_required(VERSION 3.9)+cmake_policy(SET CMP0042 NEW)+set (CMAKE_CXX_STANDARD 11)++project (build-node-addon-api-with-cmake)+include_directories(${CMAKE_JS_INC})+file(GLOB SOURCE_FILES "hello.cc")+add_library(${PROJECT_NAME} SHARED ${SOURCE_FILES} ${CMAKE_JS_SRC})+set_target_properties(${PROJECT_NAME} PROPERTIES PREFIX "" SUFFIX ".node")+target_link_libraries(${PROJECT_NAME} ${CMAKE_JS_LIB})++# Include Node-API wrappers+execute_process(COMMAND node -p "require('node-addon-api').include"+ WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}+ OUTPUT_VARIABLE NODE_ADDON_API_DIR+ )+string(REGEX REPLACE "[\r\n\"]" "" NODE_ADDON_API_DIR ${NODE_ADDON_API_DIR})++target_include_directories(${PROJECT_NAME} PRIVATE ${NODE_ADDON_API_DIR})++# define NAPI_VERSION+add_definitions(-DNAPI_VERSION=3)
src/8-tooling/build_with_cmake/node-addon-api/hello.cc
@@ -0,0 +1,23 @@+#include <napi.h>++static Napi::String Method(const Napi::CallbackInfo& info) {+ // Napi::Env is the opaque data structure containing the environment in which+ // the request is being run. We will need this env when we want to create any+ // new objects inside of the node.js environment+ Napi::Env env = info.Env();++ // Create a C++ level variable+ std::string helloWorld = "Hello, world!";++ // Return a new javascript string that we copy-construct inside of the node.js+ // environment+ return Napi::String::New(env, helloWorld);+}++static Napi::Object Init(Napi::Env env, Napi::Object exports) {+ exports.Set(Napi::String::New(env, "hello"),+ Napi::Function::New(env, Method));+ return exports;+}++NODE_API_MODULE(hello, Init)
src/8-tooling/build_with_cmake/node-addon-api/hello.js
@@ -0,0 +1,3 @@+var addon = require('bindings')('build-node-addon-api-with-cmake');++console.log(addon.hello()); // 'world'
src/8-tooling/build_with_cmake/node-addon-api/package.json
@@ -0,0 +1,15 @@+{+ "name": "build-node-addon-api-with-cmake",+ "version": "0.0.0",+ "description": "Build Node-API native addon with CMake and node-addon-api C++ wrapper.",+ "main": "hello.js",+ "private": true,+ "dependencies": {+ "bindings": "~1.2.1",+ "node-addon-api": "^1.0.0"+ },+ "scripts": {+ "install": "cmake-js compile",+ "test": "node hello.js"+ }+}
src/8-tooling/typescript_with_addon/node-addon-api/CMakeLists.txt
@@ -0,0 +1,26 @@+cmake_minimum_required(VERSION 3.9)+set (CMAKE_CXX_STANDARD 11)++project(typescript_with_addon VERSION 0.0.1)+include_directories(${CMAKE_JS_INC})+string(APPEND CMAKE_C_FLAGS "-Wall -Wextra -Wno-unused-parameter -Wendif-labels")++file(GLOB SOURCE_FILES *.cpp *.h *.cc *.c)+add_library(${PROJECT_NAME} SHARED ${SOURCE_FILES} ${CMAKE_JS_SRC})+set_target_properties(${PROJECT_NAME} PROPERTIES PREFIX "" SUFFIX ".node")+target_link_libraries(${PROJECT_NAME} ${CMAKE_JS_LIB})++# Include Node-API wrappers+execute_process(COMMAND node -p "require('node-addon-api').include"+ WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}+ OUTPUT_VARIABLE NODE_ADDON_API_DIR+ )++# need to pay attention to this part+string(REGEX REPLACE "[\r\n\"]" "" NODE_ADDON_API_DIR ${NODE_ADDON_API_DIR})++target_include_directories(${PROJECT_NAME} PRIVATE ${NODE_ADDON_API_DIR})++# Define NAPI_VERSION+add_definitions(-DNAPI_VERSION=3)+
src/8-tooling/typescript_with_addon/node-addon-api/README.md
@@ -0,0 +1,19 @@+## TypeScript calls JavaScript that calls C++ that calls C function++### Build and run:++```+npm install +npm test+```++The result:++```+This is a TypeScript class constructor.+This is a Javascript function.+This is a C++ function.+This is a C function.+```++This is using cmake and cmake-js based on the example [build_with_cmake](../build_with_cmake)
src/8-tooling/typescript_with_addon/node-addon-api/cPart.c
@@ -0,0 +1,8 @@+#include <stdio.h>+#include "cPart.h"++int cPart(int value) {+ int newValue = value + 30;+ printf("I'm a C function and I received %d from C++ and I'm sending back %d \n",value,newValue);+ return newValue;+}
src/8-tooling/typescript_with_addon/node-addon-api/cPart.h
@@ -0,0 +1,14 @@+#ifndef _C_PART_H_+#define _C_PART_H_++#ifdef __cplusplus+extern "C" {+#endif++int cPart(int);++#ifdef __cplusplus+} // extern "C"+#endif++#endif
src/8-tooling/typescript_with_addon/node-addon-api/cppPart.cpp
@@ -0,0 +1,11 @@+#include <iostream>+#include "cPart.h"++using namespace std;++int callingCPart(int value) {+ int result = cPart(++value);+ cout << "I'm a C++ function I'm sending " << value << " to C "+ << "and I received " << result << endl;+ return result;+}
src/8-tooling/typescript_with_addon/node-addon-api/index.ts
@@ -0,0 +1,11 @@+import {javascriptPart} from './jsPart';++class TypeScriptPart {+ constructor(value:number) {+ console.log('I\'m a TypeScript class constructor I\'m sending',+ value,+ 'to JavaScript and I received', javascriptPart(value));+ }+}++new TypeScriptPart(10);
src/8-tooling/typescript_with_addon/node-addon-api/jsPart.js
@@ -0,0 +1,16 @@+const addon = require('bindings')('typescript_with_addon');++function javascriptPart (value) {+ const result = addon.cppPartExportedByNapi(++value);++ console.log('I\'m a Javascript function and I\'m sending', + value,+ 'to C++ and I received',+ result);++ return result;+}++module.exports = {+ javascriptPart+}
src/8-tooling/typescript_with_addon/node-addon-api/napiPart.cpp
@@ -0,0 +1,18 @@+#include <napi.h>++extern int callingCPart(int value);++Napi::Value Method(const Napi::CallbackInfo& info) {+ Napi::Env env = info.Env();+ int value = info[0].As<Napi::Number>().Int32Value();+ Napi::Number num = Napi::Number::New(env, callingCPart(value));+ return num;+}++Napi::Object Init(Napi::Env env, Napi::Object exports) {+ exports.Set(Napi::String::New(env, "cppPartExportedByNapi"),+ Napi::Function::New(env, Method));+ return exports;+}++NODE_API_MODULE(cppPartExportedByNapi, Init)
src/8-tooling/typescript_with_addon/node-addon-api/package.json
@@ -0,0 +1,17 @@+{+ "name": "typescript_with_addon",+ "version": "0.0.0",+ "description": "TypeScript calling Javascript calling C++ calling C with building with CMake and node-addon-api C++ wrapper.",+ "dependencies": {+ "bindings": "^1.5.0",+ "node-addon-api": "^3.0.2"+ },+ "scripts": {+ "install": "npx cmake-js compile",+ "test": "ts-node index.ts"+ },+ "devDependencies": {+ "ts-node": "^9.0.0",+ "typescript": "^5.2.2"+ }+}
src/8-tooling/typescript_with_addon/node-addon-api/tsconfig.json
@@ -0,0 +1,7 @@+{+ "compilerOptions": {+ "moduleResolution": "node",+ "module": "commonjs"+ }+}+
@@ -1,49 +1,90 @@-const fs = require('fs')-const path = require('path')-const { execSync } = require('child_process')-const chalk = require('chalk')-const semver = require('semver')--const excludeFolder = ['node_modules', 'website']--function getAllTests() {- return fs- .readdirSync('./')- .filter(i => {- return (- !i.startsWith('.') &&- fs.statSync(i).isDirectory() &&- !excludeFolder.includes(i)- )- })- .map(i => {- const p = path.join(__dirname, i)- const tests = fs- .readdirSync(p)- .filter(j => fs.statSync(path.join(p, j)).isDirectory())- .map(j => path.join(p, j))- return tests- })+const fs = require("fs");+const path = require("path");+const { execSync } = require("child_process");+const chalk = require("chalk");+const semver = require("semver");++const examplesFolder = path.join(__dirname, "src");++function getAllExamples(pathToCheck) {+ const directoriesToTest = [];+ for (const fd of fs.readdirSync(pathToCheck)) {+ const absPath = path.join(pathToCheck, fd);+ if (fs.existsSync(path.join(absPath, "package.json"))) {+ directoriesToTest.push(absPath);+ continue;+ }+ if (fs.statSync(absPath).isDirectory()) {+ directoriesToTest.push(...getAllExamples(absPath));+ }+ }+ return directoriesToTest; } -getAllTests().map(tests => {- tests.map(i => {- console.log(chalk.green(`testing: ${i}`))- const p = require(path.join(i, 'package.json'))- if (p.engines && p.engines.node) {- const currentNodeVersion = process.versions.node- const range = p.engines.node- const engineOk = semver.satisfies(currentNodeVersion, range)- if (!engineOk) {- console.warn(- chalk.yellow(`${i} require Node.js ${range}, current is ${currentNodeVersion}, skipping`)+const passed = [];+const failedInstalls = [];+const noTest = [];+const failedTests = [];+for (directoryToTest of getAllExamples(examplesFolder)) {+ console.log(chalk.green(`testing: ${directoryToTest}`));+ const pkgJson = require(path.join(directoryToTest, "package.json"));+ if (pkgJson.engines && pkgJson.engines.node) {+ const currentNodeVersion = process.versions.node;+ const range = pkgJson.engines.node;+ const engineOk = semver.satisfies(currentNodeVersion, range);+ if (!engineOk) {+ console.warn(+ chalk.yellow(+ `${directoryToTest} require Node.js ${range}, current is ${currentNodeVersion}, skipping` )- return- }+ );+ continue; }- const stdout = execSync('npm install', {- cwd: i- })- console.log(stdout.toString())- })-})+ }++ try {+ const stdout = execSync("npm install", { cwd: directoryToTest });+ console.log(stdout.toString());+ } catch (err) {+ console.log(err);+ failedInstalls.push(directoryToTest);+ continue;+ }++ let testCommand;+ if ("scripts" in pkgJson && "start" in pkgJson.scripts) {+ testCommand = "npm start";+ } else if ("scripts" in pkgJson && "test" in pkgJson.scripts) {+ testCommand = "npm test";+ } else if ("main" in pkgJson) {+ testCommand = `node ${pkgJson.main}`+ } else {+ noTest.push(directoryToTest);+ continue;+ }++ try {+ const stdout = execSync(testCommand, { cwd: directoryToTest });+ console.log(stdout.toString());+ passed.push(directoryToTest);+ } catch (err) {+ console.log(err);+ failedTests.push(directoryToTest);+ }+}++passed.map((dir) => console.log(chalk.green(`passed: ${dir}`)));++if (noTest.length > 0) {+ console.warn(chalk.yellow("no test found:"));+ noTest.map((dir) => console.warn(chalk.yellow(` ${dir}`)));+}++if (failedInstalls.length > 0) {+ console.error(chalk.red("failed to install:"));+ failedInstalls.map((dir) => console.warn(chalk.red(` ${dir}`)));+}+if (failedTests.length > 0) {+ console.error(chalk.red("failed tests:"));+ failedTests.map((dir) => console.warn(chalk.red(` ${dir}`)));+}
thread_safe_function_counting/node-addon-api/addon.cc
@@ -1,106 +0,0 @@-#include <chrono>-#include <thread>-#include "napi.h"--constexpr size_t ARRAY_LENGTH = 10;--// Data structure representing our thread-safe function context.-struct TsfnContext {- TsfnContext(Napi::Env env) : deferred(Napi::Promise::Deferred::New(env)) {- for (size_t i = 0; i < ARRAY_LENGTH; ++i) ints[i] = i;- };-- // Native Promise returned to JavaScript- Napi::Promise::Deferred deferred;-- // Native thread- std::thread nativeThread;-- // Some data to pass around- int ints[ARRAY_LENGTH];-- Napi::ThreadSafeFunction tsfn;-};--// The thread entry point. This takes as its arguments the specific-// threadsafe-function context created inside the main thread.-void threadEntry(TsfnContext* context);--// The thread-safe function finalizer callback. This callback executes-// at destruction of thread-safe function, taking as arguments the finalizer-// data and threadsafe-function context.-void FinalizerCallback(Napi::Env env, void* finalizeData, TsfnContext* context);--// Exported JavaScript function. Creates the thread-safe function and native-// thread. Promise is resolved in the thread-safe function's finalizer.-Napi::Value CreateTSFN(const Napi::CallbackInfo& info) {- Napi::Env env = info.Env();-- // Construct context data- auto testData = new TsfnContext(env);-- // Create a new ThreadSafeFunction.- testData->tsfn = Napi::ThreadSafeFunction::New(- env, // Environment- info[0].As<Napi::Function>(), // JS function from caller- "TSFN", // Resource name- 0, // Max queue size (0 = unlimited).- 1, // Initial thread count- testData, // Context,- FinalizerCallback, // Finalizer- (void*)nullptr // Finalizer data- );- testData->nativeThread = std::thread(threadEntry, testData);-- // Return the deferred's Promise. This Promise is resolved in the thread-safe- // function's finalizer callback.- return testData->deferred.Promise();-}--// The thread entry point. This takes as its arguments the specific-// threadsafe-function context created inside the main thread.-void threadEntry(TsfnContext* context) {- // This callback transforms the native addon data (int *data) to JavaScript- // values. It also receives the treadsafe-function's registered callback, and- // may choose to call it.- auto callback = [](Napi::Env env, Napi::Function jsCallback, int* data) {- jsCallback.Call({Napi::Number::New(env, *data)});- };-- for (size_t index = 0; index < ARRAY_LENGTH; ++index) {- // Perform a call into JavaScript.- napi_status status =- context->tsfn.BlockingCall(&context->ints[index], callback);-- if (status != napi_ok) {- Napi::Error::Fatal(- "ThreadEntry",- "Napi::ThreadSafeNapi::Function.BlockingCall() failed");- }- // Sleep for some time.- std::this_thread::sleep_for(std::chrono::milliseconds(200));- }-- // Release the thread-safe function. This decrements the internal thread- // count, and will perform finalization since the count will reach 0.- context->tsfn.Release();-}--void FinalizerCallback(Napi::Env env,- void* finalizeData,- TsfnContext* context) {- // Join the thread- context->nativeThread.join();-- // Resolve the Promise previously returned to JS via the CreateTSFN method.- context->deferred.Resolve(Napi::Boolean::New(env, true));- delete context;-}--// Addon entry point-Napi::Object Init(Napi::Env env, Napi::Object exports) {- exports["createTSFN"] = Napi::Function::New(env, CreateTSFN);- return exports;-}--NODE_API_MODULE(addon, Init)
thread_safe_function_counting/node-addon-api/addon.js
@@ -1,9 +0,0 @@-const { createTSFN } = require('bindings')('addon');--const callback = (...args) => { - console.log(new Date, ...args); -};--void async function() {- console.log(await createTSFN(callback));-}();
thread_safe_function_counting/node-addon-api/binding.gyp
@@ -1,21 +0,0 @@-{- 'targets': [{- 'target_name': 'addon',- 'defines': ['V8_DEPRECATION_WARNINGS=1'],- 'sources': ['addon.cc'],- 'include_dirs': ["<!@(node -p \"require('node-addon-api').include\")"],- 'dependencies': ["<!(node -p \"require('node-addon-api').gyp\")"],- 'cflags!': ['-fno-exceptions'],- 'cflags_cc!': ['-fno-exceptions'],- 'xcode_settings': {- 'GCC_ENABLE_CPP_EXCEPTIONS': 'YES',- 'CLANG_CXX_LIBRARY': 'libc++',- 'MACOSX_DEPLOYMENT_TARGET': '10.7',- },- 'msvs_settings': {- 'VCCLCompilerTool': {- 'ExceptionHandling': 1- },- },- }]-}
thread_safe_function_counting/node-addon-api/package.json
@@ -1,15 +0,0 @@-{- "name": "tsfn-counting",- "version": "0.0.0",- "description": "Thread-safe Function Example",- "main": "addon.js",- "private": true,- "gypfile": true,- "dependencies": {- "node-addon-api": "*",- "bindings": "*"- },- "engines": {- "node": ">= 10.16.0"- }-}
thread_safe_function_round_trip/napi/binding.gyp
@@ -1,8 +0,0 @@-{- 'targets': [- {- 'target_name': 'round_trip',- 'sources': [ 'round_trip.c' ]- }- ]-}
thread_safe_function_round_trip/napi/index.js
@@ -1,14 +0,0 @@-const bindings = require('bindings')('round_trip');--bindings.startThread(item => {- const thePrime = item.prime;- console.log('The prime: ' + thePrime);-- // Answer the call with a 90% probability of returning true somewhere between- // 200 and 400 ms from now.- setTimeout(() => {- const theAnswer = (Math.random() > 0.1);- console.log(thePrime + ': answering with ' + theAnswer);- bindings.registerReturnValue(item, theAnswer);- }, Math.random() * 200 + 200);-});
thread_safe_function_round_trip/napi/package.json
@@ -1,17 +0,0 @@-{- "name": "thread_safe_function_round_trip",- "version": "0.0.0",- "description": "Thread-safe Function Example With JavaScript Round Trip",- "main": "index.js",- "private": true,- "dependencies": {- "bindings": "~1.2.1"- },- "engines": {- "node": ">= 10.6.0"- },- "scripts": {- "test": "node index.js"- },- "gypfile": true-}
thread_safe_function_round_trip/napi/round_trip.c
@@ -1,427 +0,0 @@-#include <stdio.h>-#include <stdlib.h>-#include <string.h>-#include <uv.h>-#define NAPI_EXPERIMENTAL-#include <node_api.h>--#define CHECK(expr) \- { \- if ((expr) == 0) { \- fprintf(stderr, "%s:%d: failed assertion `%s'\n", __FILE__, __LINE__, #expr); \- fflush(stderr); \- abort(); \- } \- }--#define REPORT_EVERY 1000--// An item that will be generated from the thread, passed into JavaScript, and-// ultimately marked as resolved when the JavaScript passes it back into the-// addon instance with a return value.-typedef struct ThreadItem {- // This field is read-only once set, so it need not be protected by the mutex.- int the_prime;-- // This field is only accessed from the secondary thread, so it also need not- // be protected by the mutex.- struct ThreadItem* next;-- // These two values must be protected by the mutex.- bool call_has_returned;- bool return_value;-} ThreadItem;--// The data associated with an instance of the addon. This takes the place of-// global static variables, while allowing multiple instances of the addon to-// co-exist.-typedef struct {- uv_mutex_t check_status_mutex;- uv_thread_t the_thread;- napi_threadsafe_function tsfn;- napi_ref thread_item_constructor;- bool js_accepts;-} AddonData;--// This function is responsible for converting the native data coming in from-// the secondary thread to JavaScript values, and for calling the JavaScript-// function. It may also be called with `env` and `js_cb` set to `NULL` when-// Node.js is terminating and there are items coming in from the secondary-// thread left to process. In that case, this function does nothing, since it is-// the secondary thread that frees the items.-static void CallJs(napi_env env, napi_value js_cb, void* context, void* data) {- AddonData* addon_data = (AddonData*)context;- napi_value constructor;-- // The semantics of this example are such that, once the JavaScript returns- // `false`, the `ThreadItem` structures can no longer be accessed, because the- // thread terminates and frees them all. Thus, we record the instant when- // JavaScript returns `false` by setting `addon_data->js_accepts` to `false`- // in `RegisterReturnValue` below, and we use the value here to decide whether- // the data coming in from the secondary thread is stale or not.- if (addon_data->js_accepts && !(env == NULL || js_cb == NULL)) {- napi_value undefined, js_thread_item;- // Retrieve the JavaScript `undefined` value. This will serve as the `this`- // value for the function call.- CHECK(napi_get_undefined(env, &undefined) == napi_ok);-- // Retrieve the constructor for the JavaScript class from which the item- // holding the native data will be constructed.- CHECK(napi_get_reference_value(env,- addon_data->thread_item_constructor,- &constructor) == napi_ok);-- // Construct a new instance of the JavaScript class to hold the native item.- CHECK(napi_new_instance(env,- constructor,- 0,- NULL,- &js_thread_item) == napi_ok);-- // Associate the native item with the newly constructed JavaScript object.- // We assume that the JavaScript side will eventually pass this JavaScript- // object back to us via `RegisterReturnValue`, which will allow the- // eventual deallocation of the native data. That's why we do not provide a- // finalizer here.- CHECK(napi_wrap(env, js_thread_item, data, NULL, NULL, NULL) == napi_ok);-- // Call the JavaScript function with the item as wrapped into an instance of- // the JavaScript `ThreadItem` class and the prime.- CHECK(napi_call_function(env,- undefined,- js_cb,- 1,- &js_thread_item,- NULL) == napi_ok);- }-}--// When the thread is finished we join it to prevent memory leaks. We can safely-// set `addon_data->tsfn` to NULL, because the thread-safe function will be-// cleaned up in the background in response to the secondary thread having-// called `napi_release_threadsafe_function()`.-static void ThreadFinished(napi_env env, void* data, void* context) {- (void) context;- AddonData* addon_data = (AddonData*)data;- CHECK(uv_thread_join(&(addon_data->the_thread)) == 0);- addon_data->tsfn = NULL;-}--// The secondary thread produces prime numbers using a very inefficient-// algorithm and calls into JavaScript with every REPORT_EVERYth prime number.-// After each call it checks whether any of the previous calls have produced a-// return value, and, if so, whether that return value is `false`. A `false`-// return value indicates that the JavaScript side is no longer interested in-// receiving any values and that the thread and the thread-safe function are to-// be cleaned up. On the JavaScript thread, this is marked in-// `addon_data->js_accepts`. When set to `false`, the JavaScript thread will not-// access the thread items any further, so they can be safely deleted on this-// thread.-static void PrimeThread(void* data) {- AddonData* addon_data = (AddonData*) data;- int idx_outer, idx_inner;- int prime_count = 0;- ThreadItem* first = NULL;- ThreadItem* current = NULL;- ThreadItem* previous = NULL;- ThreadItem* returned = NULL;-- // Check each integer whether it's a prime.- for (idx_outer = 2 ;; idx_outer++) {-- // Check whether `idx_outer` is divisible by anything up to and not- // including itself.- for (idx_inner = 2;- idx_inner < idx_outer && idx_outer % idx_inner != 0;- idx_inner++);-- // If we find a prime, and it is REPORT_EVERY primes away from the previous- // prime we found, then we send it to JavaScript.- if (idx_inner >= idx_outer && (++prime_count % REPORT_EVERY) == 0) {- // Create a new thread item and attach it to the list of outstanding- // `ThreadItem` structures representing calls into JavaScript for which- // no return value has yet been established.- current = memset(malloc(sizeof(*current)), 0, sizeof(*current));- current->the_prime = idx_outer;- current->call_has_returned = false;- current->return_value = false;- current->next = first;- first = current;-- // Pass the new item into JavaScript.- CHECK(napi_call_threadsafe_function(addon_data->tsfn,- first,- napi_tsfn_blocking) == napi_ok);- }-- // Pass over all outstanding thread items and check whether any of them have- // returned.- for (current = first, previous = NULL, returned = NULL;- current != NULL && returned == NULL;- previous = current,- current = current->next) {- uv_mutex_lock(&(addon_data->check_status_mutex));- if (current->call_has_returned) {- // Unhook the call that has returned from the list.- if (previous != NULL) {- previous->next = current->next;- } else {- first = current->next;- }- returned = current;- }- uv_mutex_unlock(&(addon_data->check_status_mutex));- }-- // Process a return value. Free the `ThreadItem` that returned it, and break- // out of the loop if the return value was `false`.- if (returned != NULL) {- // Save the return value to a local variable because we have to check it- // after having freed the structure wherein it is stored.- bool return_value = returned->return_value;- free(returned);- if (!return_value) {- break;- }- }- }-- // Before terminating the thread we free the remaining queue items. `CallJs`- // will be called with pointers to these items, perhaps after this thread has- // already freed them, but that's OK, because on the JavaScript thread- // `addon_data->js_accepts` will certainly have been set to `false`, and so- // `CallJs` will not dereference the stale pointers.- for (current = first; current != NULL;) {- previous = current;- current = current->next;- free(previous);- }-- // Release the thread-safe function. This causes it to be cleaned up in the- // background.- CHECK(napi_release_threadsafe_function(addon_data->tsfn,- napi_tsfn_release) == napi_ok);-}--// This binding can be called from JavaScript to start the asynchronous prime-// generator.-static napi_value StartThread(napi_env env, napi_callback_info info) {- size_t argc = 1;- napi_value js_cb, work_name;- AddonData* addon_data;-- // The binding accepts one parameter - the JavaScript callback function to- // call.- CHECK(napi_get_cb_info(env,- info,- &argc,- &js_cb,- NULL,- (void*)&addon_data) == napi_ok);-- // We do not create a second thread if one is already running.- CHECK(addon_data->tsfn == NULL && "Work already in progress");-- addon_data->js_accepts = true;-- // This string describes the asynchronous work.- CHECK(napi_create_string_utf8(env,- "Thread-safe Function Round Trip Example",- NAPI_AUTO_LENGTH,- &work_name) == napi_ok);-- // The thread-safe function will be created with an unlimited queue and with- // an initial thread count of 1. The secondary thread will release the- // thread-safe function, decreasing its thread count to 0, thereby setting off- // the process of cleaning up the thread-safe function.- CHECK(napi_create_threadsafe_function(env,- js_cb,- NULL,- work_name,- 0,- 1,- addon_data,- ThreadFinished,- addon_data,- CallJs,- &addon_data->tsfn) == napi_ok);-- // Create the thread that will produce primes and that will call into- // JavaScript using the thread-safe function.- CHECK(uv_thread_create(&(addon_data->the_thread), PrimeThread, addon_data) == 0);-- return NULL;-}--static bool-is_thread_item (napi_env env, napi_ref constructor_ref, napi_value value) {- bool validate;- napi_value constructor;- CHECK(napi_get_reference_value(env,- constructor_ref,- &constructor) == napi_ok);- CHECK(napi_instanceof(env, value, constructor, &validate) == napi_ok);- return validate;-}--// We use a separate binding to register a return value for a given call into-// JavaScript, represented by a `ThreadItem` object on both the JavaScript side-// and the native side. This allows the JavaScript side to asynchronously-// determine the return value.-static napi_value RegisterReturnValue(napi_env env, napi_callback_info info) {- // This function accepts two parameters:- // 1. The thread item passed into JavaScript via `CallJs`, and- // 2. The desired return value.- size_t argc = 2;- napi_value argv[2];- AddonData* addon_data;- bool return_value;- ThreadItem* item;-- // Retrieve the parameters with which this function was called.- CHECK(napi_get_cb_info(env,- info,- &argc,- argv,- NULL,- (void*)&addon_data) == napi_ok);-- // If this function recorded a return value of `false` before, it means that- // the thread and the associated thread-safe function are shutting down. This,- // in turn means that the wrapped `ThreadItem` that was received in the first- // argument may also be stale. Our best strategy is to do nothing and return- // to JavaScript.- if (!addon_data->js_accepts) {- return NULL;- }-- CHECK(argc == 2 && "Exactly two arguments were received");-- // Make sure the first parameter is an instance of the `ThreadItem` class.- // This type check ensures that there *is* a pointer stored inside the- // JavaScript object, and that the pointer is to a `ThreadItem` structure.- CHECK(is_thread_item(env, addon_data->thread_item_constructor, argv[0]));-- // Retrieve the native data from the item.- CHECK(napi_unwrap(env, argv[0], (void**)&item) == napi_ok);-- // Retrieve the desired return value.- CHECK(napi_get_value_bool(env, argv[1], &return_value) == napi_ok);-- // Set `js_accepts` to false in case the JavaScript callback returned false.- if (addon_data->js_accepts) {- addon_data->js_accepts = return_value;- }-- // Mark the thread item as resolved, and record the JavaScript return value.- uv_mutex_lock(&(addon_data->check_status_mutex));- item->call_has_returned = true;- item->return_value = return_value;- uv_mutex_unlock(&(addon_data->check_status_mutex));-- return NULL;-}--// Constructor for instances of the `ThreadItem` class. This doesn't need to do-// anything since all we want the class for is to be able to type-check-// JavaScript objects that carry within them a pointer to a native `ThreadItem`-// structure.-static napi_value ThreadItemConstructor(napi_env env, napi_callback_info info) {- return NULL;-}--// Getter for the `prime` property of the `ThreadItem` class.-static napi_value GetPrime(napi_env env, napi_callback_info info) {- napi_value jsthis, prime_property;- AddonData* ad;- CHECK(napi_ok == napi_get_cb_info(env, info, 0, 0, &jsthis, (void*)&ad));- CHECK(is_thread_item(env, ad->thread_item_constructor, jsthis));- ThreadItem* item;- CHECK(napi_ok == napi_unwrap(env, jsthis, (void**)&item));- CHECK(napi_ok == napi_create_int32(env, item->the_prime, &prime_property));- return prime_property;-}--static void addon_is_unloading(napi_env env, void* data, void* hint) {- AddonData* addon_data = (AddonData*)data;- uv_mutex_destroy(&(addon_data->check_status_mutex));- CHECK(napi_delete_reference(env,- addon_data->thread_item_constructor) == napi_ok);- free(data);-}--// Initialize an instance of this addon. This function may be called multiple-// times if multiple instances of Node.js are running on multiple threads, or if-// there are multiple Node.js contexts running on the same thread. The return-// value and the formal parameters in comments remind us that the function body-// that follows, within which we initialize the addon, has available to it the-// variables named in the formal parameters, and that it must return a-// `napi_value`.-/*napi_value*/ NAPI_MODULE_INIT(/*napi_env env, napi_value exports*/) {- // Create the native data that will be associated with this instance of the- // addon.- AddonData* addon_data =- memset(malloc(sizeof(*addon_data)), 0, sizeof(*addon_data));-- // Attach the addon data to the exports object to ensure that they are- // destroyed together.- CHECK(napi_wrap(env,- exports,- addon_data,- addon_is_unloading,- NULL,- NULL) == napi_ok);-- // Initialize the various members of the `AddonData` associated with this- // addon instance.- CHECK(uv_mutex_init(&(addon_data->check_status_mutex)) == 0);-- napi_value thread_item_class;- napi_property_descriptor thread_item_properties[] = {- { "prime", 0, 0, GetPrime, 0, 0, napi_enumerable, addon_data }- };- CHECK(napi_define_class(env,- "ThreadItem",- NAPI_AUTO_LENGTH,- ThreadItemConstructor,- addon_data,- 1,- thread_item_properties,- &thread_item_class) == napi_ok);- CHECK(napi_create_reference(env,- thread_item_class,- 1,- &(addon_data->thread_item_constructor)) ==- napi_ok);-- // Expose the two bindings this addon provides.- napi_property_descriptor export_properties[] = {- {- "startThread",- NULL,- StartThread,- NULL,- NULL,- NULL,- napi_default,- addon_data- },- {- "registerReturnValue",- NULL,- RegisterReturnValue,- NULL,- NULL,- NULL,- napi_default,- addon_data- }- };- CHECK(napi_define_properties(env,- exports,- sizeof(export_properties) /- sizeof(export_properties[0]),- export_properties) == napi_ok);-- return exports;-}
thread_safe_function_with_object_wrap/node-addon-api/binding.gyp
@@ -1,17 +0,0 @@-{- "targets": [- {- "target_name": "tsfn_object_wrap",- "sources": [- "tsfn_object_wrap.cc",- ],- "defines": [- "NAPI_DISABLE_CPP_EXCEPTIONS",- "NODE_API_SWALLOW_UNTHROWABLE_EXCEPTIONS"- ],- "include_dirs": [- "<!@(node -p \"require('node-addon-api').include\")"- ],- }- ]-}
thread_safe_function_with_object_wrap/node-addon-api/package.json
@@ -1,14 +0,0 @@-{- "name": "tsfn-test",- "version": "1.0.0",- "description": "",- "main": "index.js",- "scripts": {- "test": "echo \"Error: no test specified\" && exit 1"- },- "author": "",- "dependencies": {- "bindings": "*",- "node-addon-api": "^7.0.0"- }-}
thread_safe_function_with_object_wrap/node-addon-api/tsfn_object_wrap.cc
@@ -1,77 +0,0 @@-#include <inttypes.h>-#include <napi.h>-#include <thread>--// A secondary thread increments a value starting from 0 and calls a JS callback-// on the JavaScript main thread.--// The ObjectWrap subclass whose instances own such a secondary thread.-class TsfnObjectWrap : public Napi::ObjectWrap<TsfnObjectWrap> {- public:- static Napi::Object Init(Napi::Env env) {- return DefineClass(env, "TsfnObjectWrap", {});- }-- TsfnObjectWrap(const Napi::CallbackInfo& info)- : Napi::ObjectWrap<TsfnObjectWrap>(info) {- // Whenever we construct a new instance of type TsfnObjectWrap, we construct- // a thread-safe function that can be called from a secondary thread and- // which provides a value to the JavaScript main thread.- _tsfn = Napi::ThreadSafeFunction::New(- info.Env(), info[0].As<Napi::Function>(), "TsfnObjectWrap", 1, 2);-- _thread = std::thread(&TsfnObjectWrap::Thread, std::ref(_tsfn));- }-- ~TsfnObjectWrap() {- _tsfn.Abort();- _thread.join();- }-- private:- // This is the secondary thread.- static void Thread(const Napi::ThreadSafeFunction& tsfn) {- int64_t the_value = 0;- int64_t buffer[3] = {0, 0, 0};- int idx = 0;-- // Since we're calling the JavaScript main thread in a blocking fashion,- // a buffer of three values is sufficient for synchronizing with the main- // thread without losing a single value and without having to allocate an- // integer on the heap for each call into JavaScript.- while (true) {- buffer[idx] = the_value;- idx = (idx + 1) % 3;- int64_t* value_ref = &buffer[idx];- napi_status status = tsfn.BlockingCall(- value_ref, [](Napi::Env env, Napi::Function fn, int64_t* data) {- int64_t native_value = *data;- Napi::Value result =- fn.Call({Napi::Number::New(env, native_value)});- if (result.IsEmpty()) {- printf("main with %" PRId64 ": result was empty!\n",- native_value);- } else {- printf("main with %" PRId64 ": Done!\n",- result.As<Napi::Number>().Int64Value());- }- });- // We break out of the infinite loop when we're informed that the thread-- // safe function is being torn down.- if (status == napi_closing) {- break;- }- the_value++;- }- }-- Napi::ThreadSafeFunction _tsfn;- std::thread _thread;-};--// Boilerplate code to define an add-on that consists of the above class.-Napi::Object TsfnObjectWrapExampleInit(Napi::Env env, Napi::Object exports) {- return TsfnObjectWrap::Init(env);-}--NODE_API_MODULE(TsfnObjectWrapExample, TsfnObjectWrapExampleInit)
thread_safe_function_with_object_wrap/node-addon-api/tsfn_object_wrap.js
@@ -1,18 +0,0 @@-const TsfnObjectWrap = require('bindings')('tsfn_object_wrap.node')-const x = new TsfnObjectWrap((value) => {- // Do something, anything, with x to keep it in scope, otherwise the instance- // will be collected and the process will exit.- x.someProperty = value;-- console.log('JS1: Called with ' + value);- return -value;-});--const y = new TsfnObjectWrap((value) => {- // Do something, anything, with y to keep it in scope, otherwise the instance- // will be collected and the process will exit.- y.someProperty = value;-- console.log('JS2: Called with ' + value);- return -value;-});
threadsafe-async-iterator/node-addon-api/CMakeLists.txt
@@ -1,23 +0,0 @@-project (example)-include_directories(${CMAKE_JS_INC} node_modules/node-addon-api/)-cmake_minimum_required(VERSION 3.18)--set(CMAKE_CXX_STANDARD 20)-set(CMAKE_CXX_STANDARD_REQUIRED ON)-include_directories(${CMAKE_JS_INC})-file(GLOB SOURCE_FILES "*.cc")-add_library(${PROJECT_NAME} SHARED ${SOURCE_FILES} ${CMAKE_JS_SRC})-set_target_properties(${PROJECT_NAME} PROPERTIES PREFIX "" SUFFIX ".node")-target_link_libraries(${PROJECT_NAME} ${CMAKE_JS_LIB})--# Include Node-API wrappers-execute_process(COMMAND node -p "require('node-addon-api').include"- WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}- OUTPUT_VARIABLE NODE_ADDON_API_DIR- )-string(REGEX REPLACE "[\r\n\"]" "" NODE_ADDON_API_DIR ${NODE_ADDON_API_DIR})--target_include_directories(${PROJECT_NAME} PRIVATE ${NODE_ADDON_API_DIR})--# define NAPI_VERSION-add_definitions(-DNAPI_VERSION=6)
threadsafe-async-iterator/node-addon-api/example.cc
@@ -1,163 +0,0 @@-#include <napi.h>-#include <condition_variable>-#include <functional>-#include <memory>-#include <mutex>-#include <optional>-#include <thread>--using namespace Napi;--class ThreadSafeAsyncIteratorExample- : public ObjectWrap<ThreadSafeAsyncIteratorExample> {- public:- ThreadSafeAsyncIteratorExample(const CallbackInfo& info)- : ObjectWrap<ThreadSafeAsyncIteratorExample>(info),- _current(info[0].As<Number>()),- _last(info[1].As<Number>()) {}-- static Object Init(Napi::Env env, Napi::Object exports);-- Napi::Value Iterator(const CallbackInfo& info);-- private:- using Context = ThreadSafeAsyncIteratorExample;-- struct DataType {- std::unique_ptr<Promise::Deferred> deferred;- bool done;- std::optional<int> value;- };-- static void CallJs(Napi::Env env,- Function callback,- Context* context,- DataType* data);-- using TSFN = TypedThreadSafeFunction<Context, DataType, CallJs>;-- using FinalizerDataType = void;-- int _current;- int _last;- TSFN _tsfn;- std::thread _thread;- std::unique_ptr<Promise::Deferred> _deferred;-- // Thread-safety- std::mutex _mtx;- std::condition_variable _cv;-- void threadEntry();-- static void FinalizerCallback(Napi::Env env,- void*,- ThreadSafeAsyncIteratorExample* context);-};--Object ThreadSafeAsyncIteratorExample::Init(Napi::Env env,- Napi::Object exports) {- Napi::Function func =- DefineClass(env,- "ThreadSafeAsyncIteratorExample",- {InstanceMethod(Napi::Symbol::WellKnown(env, "asyncIterator"),- &ThreadSafeAsyncIteratorExample::Iterator)});-- exports.Set("ThreadSafeAsyncIteratorExample", func);- return exports;-}--Napi::Value ThreadSafeAsyncIteratorExample::Iterator(const CallbackInfo& info) {- auto env = info.Env();-- if (_thread.joinable()) {- Napi::Error::New(env, "Concurrent iterations not implemented.")- .ThrowAsJavaScriptException();- return Napi::Value();- }-- _tsfn =- TSFN::New(info.Env(),- "tsfn",- 0,- 1,- this,- std::function<decltype(FinalizerCallback)>(FinalizerCallback));-- // To prevent premature garbage collection; Unref in TFSN finalizer- Ref();-- // Create thread- _thread = std::thread(&ThreadSafeAsyncIteratorExample::threadEntry, this);-- // Create iterable- auto iterable = Napi::Object::New(env);-- iterable["next"] =- Function::New(env, [this](const CallbackInfo& info) -> Napi::Value {- std::lock_guard<std::mutex> lk(_mtx);- auto env = info.Env();- if (_deferred) {- Napi::Error::New(env, "Concurrent iterations not implemented.")- .ThrowAsJavaScriptException();- return Napi::Value();- }- _deferred = std::make_unique<Promise::Deferred>(env);- _cv.notify_all();- return _deferred->Promise();- });-- return iterable;-}--void ThreadSafeAsyncIteratorExample::threadEntry() {- while (true) {- std::unique_lock<std::mutex> lk(_mtx);- _cv.wait(lk, [this] { return this->_deferred != nullptr; });- auto done = _current > _last;- if (done) {- _tsfn.BlockingCall(new DataType{std::move(this->_deferred), true, {}});- break;- } else {- std::this_thread::sleep_for(- std::chrono::seconds(1)); // Simulate CPU-intensive work- _tsfn.BlockingCall(- new DataType{std::move(this->_deferred), false, _current++});- }- }- _tsfn.Release();-}--void ThreadSafeAsyncIteratorExample::CallJs(Napi::Env env,- Function callback,- Context* context,- DataType* data) {- if (env != nullptr) {- auto value = Object::New(env);-- if (data->done) {- value["done"] = Boolean::New(env, true);- } else {- value["done"] = Boolean::New(env, false);- value["value"] = Number::New(env, data->value.value());- }- data->deferred->Resolve(value);- }-- if (data != nullptr) {- delete data;- }-}--void ThreadSafeAsyncIteratorExample::FinalizerCallback(- Napi::Env env, void*, ThreadSafeAsyncIteratorExample* context) {- context->_thread.join();- context->Unref();-}--Napi::Object Init(Napi::Env env, Object exports) {- ThreadSafeAsyncIteratorExample::Init(env, exports);- return exports;-}--NODE_API_MODULE(example, Init)
threadsafe-async-iterator/node-addon-api/index.js
@@ -1,14 +0,0 @@-const { ThreadSafeAsyncIteratorExample } = require('bindings')('example');--async function main(from, to) {- const iterator = new ThreadSafeAsyncIteratorExample(from, to);- for await (const value of iterator) {- console.log(value);- }-}--main(0, 5)- .catch(e => {- console.error(e);- process.exit(1);- });
threadsafe-async-iterator/node-addon-api/package.json
@@ -1,16 +0,0 @@-{- "name": "threadsafe-async-iterator-example",- "version": "0.0.0",- "description": "Async iterator example with threadsafe functions using node-addon-api",- "main": "index.js",- "private": true,- "dependencies": {- "bindings": "^1.5.0",- "cmake-js": "^6.3.0",- "node-addon-api": "^5.0.0"- },- "scripts": {- "test": "node index.js",- "install": "cmake-js compile"- }-}
typed_array_to_native/node-addon-api/binding.gyp
@@ -1,14 +0,0 @@-{- "targets": [- {- "target_name": "typed_array_to_native",- "cflags!": [ "-fno-exceptions" ],- "cflags_cc!": [ "-fno-exceptions" ],- "sources": [ "typed_array_to_native.cc" ],- "include_dirs": [- "<!@(node -p \"require('node-addon-api').include\")"- ],- 'defines': [ 'NAPI_DISABLE_CPP_EXCEPTIONS' ],- }- ]-}
typed_array_to_native/node-addon-api/index.js
@@ -1,15 +0,0 @@-const binding = require('bindings')('typed_array_to_native');--{- const byteArray = Uint8Array.from([10, 20, 30, 50, 25, 17]);- console.log("byteArray: ", byteArray);- binding.AcceptByteArray(byteArray);-}-console.log("---------");--{- const array = [7, 15, 26, 58, 64];- console.log("array: ", array);- const byteArray = binding.CreateByteArray(array);- console.log("byteArray: ", byteArray);-}
typed_array_to_native/node-addon-api/package.json
@@ -1,14 +0,0 @@-{- "name": "typed_array_to_native",- "version": "0.0.0",- "description": "Shows how to read and create typed arrays",- "main": "index.js",- "private": true,- "dependencies": {- "bindings": "~1.2.1",- "node-addon-api": "^1.0.0"- },- "scripts": {- "test": "node index.js"- }-}
typed_array_to_native/node-addon-api/typed_array_to_native.cc
@@ -1,108 +0,0 @@-#include <napi.h>-#include <cstdio>--static Napi::Value AcceptByteArray(const Napi::CallbackInfo& info) {- if (info.Length() != 1) {- Napi::Error::New(info.Env(), "Expected exactly one argument")- .ThrowAsJavaScriptException();- return info.Env().Undefined();- }-- if (!info[0].IsTypedArray()) {- Napi::Error::New(info.Env(), "Expected a TypedArray")- .ThrowAsJavaScriptException();- return info.Env().Undefined();- }- Napi::TypedArray typedArray = info[0].As<Napi::TypedArray>();-- if (typedArray.TypedArrayType() != napi_uint8_array) {- Napi::Error::New(info.Env(), "Expected an Uint8Array")- .ThrowAsJavaScriptException();- return info.Env().Undefined();- }- Napi::Uint8Array uint8Array = typedArray.As<Napi::Uint8Array>();-- // Copy to std::vector<uint8_t>:- std::vector<uint8_t> bytes(uint8Array.Data(),- uint8Array.Data() + uint8Array.ElementLength());- printf("std::vector<uint8_t> from Uint8Array: [");- for (uint8_t byte : bytes) {- printf("%d, ", byte);- }- printf("\b\b]\n");-- return info.Env().Undefined();-}--static Napi::Value CreateByteArray(const Napi::CallbackInfo& info) {- if (info.Length() != 1) {- Napi::Error::New(info.Env(), "Expected exactly one argument")- .ThrowAsJavaScriptException();- return info.Env().Undefined();- }-- if (!info[0].IsArray()) {- Napi::Error::New(info.Env(), "Expected an Array")- .ThrowAsJavaScriptException();- return info.Env().Undefined();- }- Napi::Array array = info[0].As<Napi::Array>();- size_t arrayLength = array.Length();-- // Create std::vector<uint8_t> out of array.- // We allocate it on the heap to allow wrapping it up into ArrayBuffer.- std::unique_ptr<std::vector<uint8_t>> nativeArray =- std::make_unique<std::vector<uint8_t>>(arrayLength, 0);- for (size_t i = 0; i < arrayLength; ++i) {- Napi::Value arrayItem = array[i];- if (!arrayItem.IsNumber()) {- Napi::Error::New(info.Env(), "Expected a Number as Array Item")- .ThrowAsJavaScriptException();- return info.Env().Undefined();- }- Napi::Number number = arrayItem.As<Napi::Number>();- double numberValue = number.DoubleValue();- if (numberValue < 0 || numberValue > 255) {- Napi::Error::New(info.Env(),- "Array Item Number value is out of range [0..255]")- .ThrowAsJavaScriptException();- return info.Env().Undefined();- }-- (*nativeArray)[i] = static_cast<uint8_t>(numberValue);- }-- printf("std::vector<uint8_t> from Array: [");- for (uint8_t byte : *nativeArray) {- printf("%d, ", byte);- }- printf("\b\b]\n");-- // Wrap up the std::vector into the ArrayBuffer.- // Note: instead of wrapping the std::vector we could allow ArrayBuffer to- // create internal storage that copies the std::vector, but it is less- // efficient because it requires an extra memory allocation.- Napi::ArrayBuffer arrayBuffer = Napi::ArrayBuffer::New(- info.Env(),- nativeArray->data(),- arrayLength /* size in bytes */,- [](Napi::Env /*env*/, void* /*data*/, std::vector<uint8_t>* hint) {- std::unique_ptr<std::vector<uint8_t>> vectorPtrToDelete(hint);- },- nativeArray.get());- // The finalizer is responsible for deleting the vector: release the- // unique_ptr ownership.- nativeArray.release();-- Napi::Uint8Array byteArray = Napi::Uint8Array::New(info.Env(), arrayLength, arrayBuffer, 0);- - return byteArray;-}--static Napi::Object Init(Napi::Env env, Napi::Object exports) {- exports["AcceptByteArray"] = Napi::Function::New(env, AcceptByteArray);- exports["CreateByteArray"] = Napi::Function::New(env, CreateByteArray);- return exports;-}--NODE_API_MODULE(NODE_GYP_MODULE_NAME, Init)
typed_threadsafe_function/node-addon-api/CMakeLists.txt
@@ -1,23 +0,0 @@-project (clock)-include_directories(${CMAKE_JS_INC} node_modules/node-addon-api/)-cmake_minimum_required(VERSION 3.18)--set(CMAKE_CXX_STANDARD 20)-set(CMAKE_CXX_STANDARD_REQUIRED ON)-include_directories(${CMAKE_JS_INC})-file(GLOB SOURCE_FILES "*.cc")-add_library(${PROJECT_NAME} SHARED ${SOURCE_FILES} ${CMAKE_JS_SRC})-set_target_properties(${PROJECT_NAME} PROPERTIES PREFIX "" SUFFIX ".node")-target_link_libraries(${PROJECT_NAME} ${CMAKE_JS_LIB})--# Include Node-API wrappers-execute_process(COMMAND node -p "require('node-addon-api').include"- WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}- OUTPUT_VARIABLE NODE_ADDON_API_DIR- )-string(REGEX REPLACE "[\r\n\"]" "" NODE_ADDON_API_DIR ${NODE_ADDON_API_DIR})--target_include_directories(${PROJECT_NAME} PRIVATE ${NODE_ADDON_API_DIR})--# define NAPI_VERSION-add_definitions(-DNAPI_VERSION=4)
typed_threadsafe_function/node-addon-api/clock.cc
@@ -1,94 +0,0 @@-#include <chrono>-#include <napi.h>-#include <thread>--using namespace Napi;--using Context = Reference<Value>;-using DataType = int;-void CallJs(Napi::Env env, Function callback, Context *context, DataType *data);-using TSFN = TypedThreadSafeFunction<Context, DataType, CallJs>;-using FinalizerDataType = void;--std::thread nativeThread;-TSFN tsfn;--Value Start(const CallbackInfo &info) {- Napi::Env env = info.Env();-- if (info.Length() < 2) {- throw TypeError::New(env, "Expected two arguments");- } else if (!info[0].IsFunction()) {- throw TypeError::New(env, "Expected first arg to be function");- } else if (!info[1].IsNumber()) {- throw TypeError::New(env, "Expected second arg to be number");- }-- int count = info[1].As<Number>().Int32Value();-- // Create a new context set to the the receiver (ie, `this`) of the function- // call- Context *context = new Reference<Value>(Persistent(info.This()));-- // Create a ThreadSafeFunction- tsfn = TSFN::New(- env,- info[0].As<Function>(), // JavaScript function called asynchronously- "Resource Name", // Name- 0, // Unlimited queue- 1, // Only one thread will use this initially- context,- [](Napi::Env, FinalizerDataType *,- Context *ctx) { // Finalizer used to clean threads up- nativeThread.join();- delete ctx;- });-- // Create a native thread- nativeThread = std::thread([count] {- for (int i = 0; i < count; i++) {- // Create new data- int *value = new int(clock());-- // Perform a blocking call- napi_status status = tsfn.BlockingCall(value);- if (status != napi_ok) {- // Handle error- break;- }-- std::this_thread::sleep_for(std::chrono::seconds(1));- }-- // Release the thread-safe function- tsfn.Release();- });-- return Boolean::New(env, true);-}--// Transform native data into JS data, passing it to the provided-// `callback` -- the TSFN's JavaScript function.-void CallJs(Napi::Env env, Function callback, Context *context,- DataType *data) {- // Is the JavaScript environment still available to call into, eg. the TSFN is- // not aborted- if (env != nullptr) {- // On Node-API 5+, the `callback` parameter is optional; however, this example- // does ensure a callback is provided.- if (callback != nullptr) {- callback.Call(context->Value(), {Number::New(env, *data)});- }- }- if (data != nullptr) {- // We're finished with the data.- delete data;- }-}--Napi::Object Init(Napi::Env env, Object exports) {- exports.Set("start", Function::New(env, Start));- return exports;-}--NODE_API_MODULE(clock, Init)
typed_threadsafe_function/node-addon-api/index.js
@@ -1,6 +0,0 @@-const { start } = require('bindings')('clock');--start.call(new Date(), function (clock) {- const context = this;- console.log(context, clock);-}, 5);
typed_threadsafe_function/node-addon-api/package.json
@@ -1,17 +0,0 @@-{- "name": "example-typedthreadsafefunction",- "version": "0.0.0",- "description": "node-addon-api TypedThreadSafeFunction example",- "main": "index.js",- "private": true,- "dependencies": {- "bindings": "~1.2.1",- "cmake-js": "^6.1.0",- "node-addon-api": "^4.0.0"- },- "scripts": {- "test": "node index.js",- "install": "cmake-js compile"- },- "gypfile": true-}
typescript_with_addon/node-addon-api/CMakeLists.txt
@@ -1,26 +0,0 @@-cmake_minimum_required(VERSION 3.9)-set (CMAKE_CXX_STANDARD 11)--project(typescript_with_addon VERSION 0.0.1)-include_directories(${CMAKE_JS_INC})-string(APPEND CMAKE_C_FLAGS "-Wall -Wextra -Wno-unused-parameter -Wendif-labels")--file(GLOB SOURCE_FILES *.cpp *.h *.cc *.c)-add_library(${PROJECT_NAME} SHARED ${SOURCE_FILES} ${CMAKE_JS_SRC})-set_target_properties(${PROJECT_NAME} PROPERTIES PREFIX "" SUFFIX ".node")-target_link_libraries(${PROJECT_NAME} ${CMAKE_JS_LIB})--# Include Node-API wrappers-execute_process(COMMAND node -p "require('node-addon-api').include"- WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}- OUTPUT_VARIABLE NODE_ADDON_API_DIR- )--# need to pay attention to this part-string(REGEX REPLACE "[\r\n\"]" "" NODE_ADDON_API_DIR ${NODE_ADDON_API_DIR})--target_include_directories(${PROJECT_NAME} PRIVATE ${NODE_ADDON_API_DIR})--# Define NAPI_VERSION-add_definitions(-DNAPI_VERSION=3)-
typescript_with_addon/node-addon-api/README.md
@@ -1,19 +0,0 @@-## TypeScript calls JavaScript that calls C++ that calls C function--### Build and run:--```-npm install -npm test-```--The result:--```-This is a TypeScript class constructor.-This is a Javascript function.-This is a C++ function.-This is a C function.-```--This is using cmake and cmake-js based on the example [build_with_cmake](../build_with_cmake)
typescript_with_addon/node-addon-api/cPart.c
@@ -1,8 +0,0 @@-#include <stdio.h>-#include "cPart.h"--int cPart(int value) {- int newValue = value + 30;- printf("I'm a C function and I received %d from C++ and I'm sending back %d \n",value,newValue);- return newValue;-}
typescript_with_addon/node-addon-api/cPart.h
@@ -1,14 +0,0 @@-#ifndef _C_PART_H_-#define _C_PART_H_--#ifdef __cplusplus-extern "C" {-#endif--int cPart(int);--#ifdef __cplusplus-} // extern "C"-#endif--#endif
typescript_with_addon/node-addon-api/cppPart.cpp
@@ -1,11 +0,0 @@-#include <iostream>-#include "cPart.h"--using namespace std;--int callingCPart(int value) {- int result = cPart(++value);- cout << "I'm a C++ function I'm sending " << value << " to C "- << "and I received " << result << endl;- return result;-}
typescript_with_addon/node-addon-api/index.ts
@@ -1,11 +0,0 @@-import {javascriptPart} from './jsPart';--class TypeScriptPart {- constructor(value:number) {- console.log('I\'m a TypeScript class constructor I\'m sending',- value,- 'to JavaScript and I received', javascriptPart(value));- }-}--new TypeScriptPart(10);
typescript_with_addon/node-addon-api/jsPart.js
@@ -1,16 +0,0 @@-const addon = require('bindings')('typescript_with_addon');--function javascriptPart (value) {- const result = addon.cppPartExportedByNapi(++value);-- console.log('I\'m a Javascript function and I\'m sending', - value,- 'to C++ and I received',- result);-- return result;-}--module.exports = {- javascriptPart-}
typescript_with_addon/node-addon-api/napiPart.cpp
@@ -1,18 +0,0 @@-#include <napi.h>--extern int callingCPart(int value);--Napi::Value Method(const Napi::CallbackInfo& info) {- Napi::Env env = info.Env();- int value = info[0].As<Napi::Number>().Int32Value();- Napi::Number num = Napi::Number::New(env, callingCPart(value));- return num;-}--Napi::Object Init(Napi::Env env, Napi::Object exports) {- exports.Set(Napi::String::New(env, "cppPartExportedByNapi"),- Napi::Function::New(env, Method));- return exports;-}--NODE_API_MODULE(cppPartExportedByNapi, Init)
typescript_with_addon/node-addon-api/package.json
@@ -1,17 +0,0 @@-{- "name": "typescript_with_addon",- "version": "0.0.0",- "description": "TypeScript calling Javascript calling C++ calling C with building with CMake and node-addon-api C++ wrapper.",- "dependencies": {- "bindings": "^1.5.0",- "node-addon-api": "^3.0.2"- },- "scripts": {- "install": "npx cmake-js compile",- "test": "ts-node index.ts"- },- "devDependencies": {- "ts-node": "^9.0.0",- "typescript": "^5.2.2"- }-}
typescript_with_addon/node-addon-api/tsconfig.json
@@ -1,7 +0,0 @@-{- "compilerOptions": {- "moduleResolution": "node",- "module": "commonjs"- }-}-