1# C++ addons 2 3<!--introduced_in=v0.10.0--> 4 5<!-- type=misc --> 6 7_Addons_ are dynamically-linked shared objects written in C++. The 8[`require()`][require] function can load addons as ordinary Node.js modules. 9Addons provide an interface between JavaScript and C/C++ libraries. 10 11There are three options for implementing addons: Node-API, nan, or direct 12use of internal V8, libuv, and Node.js libraries. Unless there is a need for 13direct access to functionality which is not exposed by Node-API, use Node-API. 14Refer to [C/C++ addons with Node-API](n-api.md) for more information on 15Node-API. 16 17When not using Node-API, implementing addons is complicated, 18involving knowledge of several components and APIs: 19 20* [V8][]: the C++ library Node.js uses to provide the 21 JavaScript implementation. V8 provides the mechanisms for creating objects, 22 calling functions, etc. V8's API is documented mostly in the 23 `v8.h` header file (`deps/v8/include/v8.h` in the Node.js source 24 tree), which is also available [online][v8-docs]. 25 26* [libuv][]: The C library that implements the Node.js event loop, its worker 27 threads and all of the asynchronous behaviors of the platform. It also 28 serves as a cross-platform abstraction library, giving easy, POSIX-like 29 access across all major operating systems to many common system tasks, such 30 as interacting with the file system, sockets, timers, and system events. libuv 31 also provides a threading abstraction similar to POSIX threads for 32 more sophisticated asynchronous addons that need to move beyond the 33 standard event loop. Addon authors should 34 avoid blocking the event loop with I/O or other time-intensive tasks by 35 offloading work via libuv to non-blocking system operations, worker threads, 36 or a custom use of libuv threads. 37 38* Internal Node.js libraries. Node.js itself exports C++ APIs that addons can 39 use, the most important of which is the `node::ObjectWrap` class. 40 41* Node.js includes other statically linked libraries including OpenSSL. These 42 other libraries are located in the `deps/` directory in the Node.js source 43 tree. Only the libuv, OpenSSL, V8, and zlib symbols are purposefully 44 re-exported by Node.js and may be used to various extents by addons. See 45 [Linking to libraries included with Node.js][] for additional information. 46 47All of the following examples are available for [download][] and may 48be used as the starting-point for an addon. 49 50## Hello world 51 52This "Hello world" example is a simple addon, written in C++, that is the 53equivalent of the following JavaScript code: 54 55```js 56module.exports.hello = () => 'world'; 57``` 58 59First, create the file `hello.cc`: 60 61```cpp 62// hello.cc 63#include <node.h> 64 65namespace demo { 66 67using v8::FunctionCallbackInfo; 68using v8::Isolate; 69using v8::Local; 70using v8::Object; 71using v8::String; 72using v8::Value; 73 74void Method(const FunctionCallbackInfo<Value>& args) { 75 Isolate* isolate = args.GetIsolate(); 76 args.GetReturnValue().Set(String::NewFromUtf8( 77 isolate, "world").ToLocalChecked()); 78} 79 80void Initialize(Local<Object> exports) { 81 NODE_SET_METHOD(exports, "hello", Method); 82} 83 84NODE_MODULE(NODE_GYP_MODULE_NAME, Initialize) 85 86} // namespace demo 87``` 88 89All Node.js addons must export an initialization function following 90the pattern: 91 92```cpp 93void Initialize(Local<Object> exports); 94NODE_MODULE(NODE_GYP_MODULE_NAME, Initialize) 95``` 96 97There is no semi-colon after `NODE_MODULE` as it's not a function (see 98`node.h`). 99 100The `module_name` must match the filename of the final binary (excluding 101the `.node` suffix). 102 103In the `hello.cc` example, then, the initialization function is `Initialize` 104and the addon module name is `addon`. 105 106When building addons with `node-gyp`, using the macro `NODE_GYP_MODULE_NAME` as 107the first parameter of `NODE_MODULE()` will ensure that the name of the final 108binary will be passed to `NODE_MODULE()`. 109 110Addons defined with `NODE_MODULE()` can not be loaded in multiple contexts or 111multiple threads at the same time. 112 113### Context-aware addons 114 115There are environments in which Node.js addons may need to be loaded multiple 116times in multiple contexts. For example, the [Electron][] runtime runs multiple 117instances of Node.js in a single process. Each instance will have its own 118`require()` cache, and thus each instance will need a native addon to behave 119correctly when loaded via `require()`. This means that the addon 120must support multiple initializations. 121 122A context-aware addon can be constructed by using the macro 123`NODE_MODULE_INITIALIZER`, which expands to the name of a function which Node.js 124will expect to find when it loads an addon. An addon can thus be initialized as 125in the following example: 126 127```cpp 128using namespace v8; 129 130extern "C" NODE_MODULE_EXPORT void 131NODE_MODULE_INITIALIZER(Local<Object> exports, 132 Local<Value> module, 133 Local<Context> context) { 134 /* Perform addon initialization steps here. */ 135} 136``` 137 138Another option is to use the macro `NODE_MODULE_INIT()`, which will also 139construct a context-aware addon. Unlike `NODE_MODULE()`, which is used to 140construct an addon around a given addon initializer function, 141`NODE_MODULE_INIT()` serves as the declaration of such an initializer to be 142followed by a function body. 143 144The following three variables may be used inside the function body following an 145invocation of `NODE_MODULE_INIT()`: 146 147* `Local<Object> exports`, 148* `Local<Value> module`, and 149* `Local<Context> context` 150 151The choice to build a context-aware addon carries with it the responsibility of 152carefully managing global static data. Since the addon may be loaded multiple 153times, potentially even from different threads, any global static data stored 154in the addon must be properly protected, and must not contain any persistent 155references to JavaScript objects. The reason for this is that JavaScript 156objects are only valid in one context, and will likely cause a crash when 157accessed from the wrong context or from a different thread than the one on which 158they were created. 159 160The context-aware addon can be structured to avoid global static data by 161performing the following steps: 162 163* Define a class which will hold per-addon-instance data and which has a static 164 member of the form 165 ```cpp 166 static void DeleteInstance(void* data) { 167 // Cast `data` to an instance of the class and delete it. 168 } 169 ``` 170* Heap-allocate an instance of this class in the addon initializer. This can be 171 accomplished using the `new` keyword. 172* Call `node::AddEnvironmentCleanupHook()`, passing it the above-created 173 instance and a pointer to `DeleteInstance()`. This will ensure the instance is 174 deleted when the environment is torn down. 175* Store the instance of the class in a `v8::External`, and 176* Pass the `v8::External` to all methods exposed to JavaScript by passing it 177 to `v8::FunctionTemplate::New()` or `v8::Function::New()` which creates the 178 native-backed JavaScript functions. The third parameter of 179 `v8::FunctionTemplate::New()` or `v8::Function::New()` accepts the 180 `v8::External` and makes it available in the native callback using the 181 `v8::FunctionCallbackInfo::Data()` method. 182 183This will ensure that the per-addon-instance data reaches each binding that can 184be called from JavaScript. The per-addon-instance data must also be passed into 185any asynchronous callbacks the addon may create. 186 187The following example illustrates the implementation of a context-aware addon: 188 189```cpp 190#include <node.h> 191 192using namespace v8; 193 194class AddonData { 195 public: 196 explicit AddonData(Isolate* isolate): 197 call_count(0) { 198 // Ensure this per-addon-instance data is deleted at environment cleanup. 199 node::AddEnvironmentCleanupHook(isolate, DeleteInstance, this); 200 } 201 202 // Per-addon data. 203 int call_count; 204 205 static void DeleteInstance(void* data) { 206 delete static_cast<AddonData*>(data); 207 } 208}; 209 210static void Method(const v8::FunctionCallbackInfo<v8::Value>& info) { 211 // Retrieve the per-addon-instance data. 212 AddonData* data = 213 reinterpret_cast<AddonData*>(info.Data().As<External>()->Value()); 214 data->call_count++; 215 info.GetReturnValue().Set((double)data->call_count); 216} 217 218// Initialize this addon to be context-aware. 219NODE_MODULE_INIT(/* exports, module, context */) { 220 Isolate* isolate = context->GetIsolate(); 221 222 // Create a new instance of `AddonData` for this instance of the addon and 223 // tie its life cycle to that of the Node.js environment. 224 AddonData* data = new AddonData(isolate); 225 226 // Wrap the data in a `v8::External` so we can pass it to the method we 227 // expose. 228 Local<External> external = External::New(isolate, data); 229 230 // Expose the method `Method` to JavaScript, and make sure it receives the 231 // per-addon-instance data we created above by passing `external` as the 232 // third parameter to the `FunctionTemplate` constructor. 233 exports->Set(context, 234 String::NewFromUtf8(isolate, "method").ToLocalChecked(), 235 FunctionTemplate::New(isolate, Method, external) 236 ->GetFunction(context).ToLocalChecked()).FromJust(); 237} 238``` 239 240#### Worker support 241 242<!-- YAML 243changes: 244 - version: 245 - v14.8.0 246 - v12.19.0 247 pr-url: https://github.com/nodejs/node/pull/34572 248 description: Cleanup hooks may now be asynchronous. 249--> 250 251In order to be loaded from multiple Node.js environments, 252such as a main thread and a Worker thread, an add-on needs to either: 253 254* Be an Node-API addon, or 255* Be declared as context-aware using `NODE_MODULE_INIT()` as described above 256 257In order to support [`Worker`][] threads, addons need to clean up any resources 258they may have allocated when such a thread exists. This can be achieved through 259the usage of the `AddEnvironmentCleanupHook()` function: 260 261```cpp 262void AddEnvironmentCleanupHook(v8::Isolate* isolate, 263 void (*fun)(void* arg), 264 void* arg); 265``` 266 267This function adds a hook that will run before a given Node.js instance shuts 268down. If necessary, such hooks can be removed before they are run using 269`RemoveEnvironmentCleanupHook()`, which has the same signature. Callbacks are 270run in last-in first-out order. 271 272If necessary, there is an additional pair of `AddEnvironmentCleanupHook()` 273and `RemoveEnvironmentCleanupHook()` overloads, where the cleanup hook takes a 274callback function. This can be used for shutting down asynchronous resources, 275such as any libuv handles registered by the addon. 276 277The following `addon.cc` uses `AddEnvironmentCleanupHook`: 278 279```cpp 280// addon.cc 281#include <node.h> 282#include <assert.h> 283#include <stdlib.h> 284 285using node::AddEnvironmentCleanupHook; 286using v8::HandleScope; 287using v8::Isolate; 288using v8::Local; 289using v8::Object; 290 291// Note: In a real-world application, do not rely on static/global data. 292static char cookie[] = "yum yum"; 293static int cleanup_cb1_called = 0; 294static int cleanup_cb2_called = 0; 295 296static void cleanup_cb1(void* arg) { 297 Isolate* isolate = static_cast<Isolate*>(arg); 298 HandleScope scope(isolate); 299 Local<Object> obj = Object::New(isolate); 300 assert(!obj.IsEmpty()); // assert VM is still alive 301 assert(obj->IsObject()); 302 cleanup_cb1_called++; 303} 304 305static void cleanup_cb2(void* arg) { 306 assert(arg == static_cast<void*>(cookie)); 307 cleanup_cb2_called++; 308} 309 310static void sanity_check(void*) { 311 assert(cleanup_cb1_called == 1); 312 assert(cleanup_cb2_called == 1); 313} 314 315// Initialize this addon to be context-aware. 316NODE_MODULE_INIT(/* exports, module, context */) { 317 Isolate* isolate = context->GetIsolate(); 318 319 AddEnvironmentCleanupHook(isolate, sanity_check, nullptr); 320 AddEnvironmentCleanupHook(isolate, cleanup_cb2, cookie); 321 AddEnvironmentCleanupHook(isolate, cleanup_cb1, isolate); 322} 323``` 324 325Test in JavaScript by running: 326 327```js 328// test.js 329require('./build/Release/addon'); 330``` 331 332### Building 333 334Once the source code has been written, it must be compiled into the binary 335`addon.node` file. To do so, create a file called `binding.gyp` in the 336top-level of the project describing the build configuration of the module 337using a JSON-like format. This file is used by [node-gyp][], a tool written 338specifically to compile Node.js addons. 339 340```json 341{ 342 "targets": [ 343 { 344 "target_name": "addon", 345 "sources": [ "hello.cc" ] 346 } 347 ] 348} 349``` 350 351A version of the `node-gyp` utility is bundled and distributed with 352Node.js as part of `npm`. This version is not made directly available for 353developers to use and is intended only to support the ability to use the 354`npm install` command to compile and install addons. Developers who wish to 355use `node-gyp` directly can install it using the command 356`npm install -g node-gyp`. See the `node-gyp` [installation instructions][] for 357more information, including platform-specific requirements. 358 359Once the `binding.gyp` file has been created, use `node-gyp configure` to 360generate the appropriate project build files for the current platform. This 361will generate either a `Makefile` (on Unix platforms) or a `vcxproj` file 362(on Windows) in the `build/` directory. 363 364Next, invoke the `node-gyp build` command to generate the compiled `addon.node` 365file. This will be put into the `build/Release/` directory. 366 367When using `npm install` to install a Node.js addon, npm uses its own bundled 368version of `node-gyp` to perform this same set of actions, generating a 369compiled version of the addon for the user's platform on demand. 370 371Once built, the binary addon can be used from within Node.js by pointing 372[`require()`][require] to the built `addon.node` module: 373 374```js 375// hello.js 376const addon = require('./build/Release/addon'); 377 378console.log(addon.hello()); 379// Prints: 'world' 380``` 381 382Because the exact path to the compiled addon binary can vary depending on how 383it is compiled (i.e. sometimes it may be in `./build/Debug/`), addons can use 384the [bindings][] package to load the compiled module. 385 386While the `bindings` package implementation is more sophisticated in how it 387locates addon modules, it is essentially using a `try…catch` pattern similar to: 388 389```js 390try { 391 return require('./build/Release/addon.node'); 392} catch (err) { 393 return require('./build/Debug/addon.node'); 394} 395``` 396 397### Linking to libraries included with Node.js 398 399Node.js uses statically linked libraries such as V8, libuv, and OpenSSL. All 400addons are required to link to V8 and may link to any of the other dependencies 401as well. Typically, this is as simple as including the appropriate 402`#include <...>` statements (e.g. `#include <v8.h>`) and `node-gyp` will locate 403the appropriate headers automatically. However, there are a few caveats to be 404aware of: 405 406* When `node-gyp` runs, it will detect the specific release version of Node.js 407 and download either the full source tarball or just the headers. If the full 408 source is downloaded, addons will have complete access to the full set of 409 Node.js dependencies. However, if only the Node.js headers are downloaded, 410 then only the symbols exported by Node.js will be available. 411 412* `node-gyp` can be run using the `--nodedir` flag pointing at a local Node.js 413 source image. Using this option, the addon will have access to the full set of 414 dependencies. 415 416### Loading addons using `require()` 417 418The filename extension of the compiled addon binary is `.node` (as opposed 419to `.dll` or `.so`). The [`require()`][require] function is written to look for 420files with the `.node` file extension and initialize those as dynamically-linked 421libraries. 422 423When calling [`require()`][require], the `.node` extension can usually be 424omitted and Node.js will still find and initialize the addon. One caveat, 425however, is that Node.js will first attempt to locate and load modules or 426JavaScript files that happen to share the same base name. For instance, if 427there is a file `addon.js` in the same directory as the binary `addon.node`, 428then [`require('addon')`][require] will give precedence to the `addon.js` file 429and load it instead. 430 431## Native abstractions for Node.js 432 433Each of the examples illustrated in this document directly use the 434Node.js and V8 APIs for implementing addons. The V8 API can, and has, changed 435dramatically from one V8 release to the next (and one major Node.js release to 436the next). With each change, addons may need to be updated and recompiled in 437order to continue functioning. The Node.js release schedule is designed to 438minimize the frequency and impact of such changes but there is little that 439Node.js can do to ensure stability of the V8 APIs. 440 441The [Native Abstractions for Node.js][] (or `nan`) provide a set of tools that 442addon developers are recommended to use to keep compatibility between past and 443future releases of V8 and Node.js. See the `nan` [examples][] for an 444illustration of how it can be used. 445 446## Node-API 447 448> Stability: 2 - Stable 449 450Node-API is an API for building native addons. It is independent from 451the underlying JavaScript runtime (e.g. V8) and is maintained as part of 452Node.js itself. This API will be Application Binary Interface (ABI) stable 453across versions of Node.js. It is intended to insulate addons from 454changes in the underlying JavaScript engine and allow modules 455compiled for one version to run on later versions of Node.js without 456recompilation. Addons are built/packaged with the same approach/tools 457outlined in this document (node-gyp, etc.). The only difference is the 458set of APIs that are used by the native code. Instead of using the V8 459or [Native Abstractions for Node.js][] APIs, the functions available 460in the Node-API are used. 461 462Creating and maintaining an addon that benefits from the ABI stability 463provided by Node-API carries with it certain 464[implementation considerations][]. 465 466To use Node-API in the above "Hello world" example, replace the content of 467`hello.cc` with the following. All other instructions remain the same. 468 469```cpp 470// hello.cc using Node-API 471#include <node_api.h> 472 473namespace demo { 474 475napi_value Method(napi_env env, napi_callback_info args) { 476 napi_value greeting; 477 napi_status status; 478 479 status = napi_create_string_utf8(env, "world", NAPI_AUTO_LENGTH, &greeting); 480 if (status != napi_ok) return nullptr; 481 return greeting; 482} 483 484napi_value init(napi_env env, napi_value exports) { 485 napi_status status; 486 napi_value fn; 487 488 status = napi_create_function(env, nullptr, 0, Method, nullptr, &fn); 489 if (status != napi_ok) return nullptr; 490 491 status = napi_set_named_property(env, exports, "hello", fn); 492 if (status != napi_ok) return nullptr; 493 return exports; 494} 495 496NAPI_MODULE(NODE_GYP_MODULE_NAME, init) 497 498} // namespace demo 499``` 500 501The functions available and how to use them are documented in 502[C/C++ addons with Node-API](n-api.md). 503 504## Addon examples 505 506Following are some example addons intended to help developers get started. The 507examples use the V8 APIs. Refer to the online [V8 reference][v8-docs] 508for help with the various V8 calls, and V8's [Embedder's Guide][] for an 509explanation of several concepts used such as handles, scopes, function 510templates, etc. 511 512Each of these examples using the following `binding.gyp` file: 513 514```json 515{ 516 "targets": [ 517 { 518 "target_name": "addon", 519 "sources": [ "addon.cc" ] 520 } 521 ] 522} 523``` 524 525In cases where there is more than one `.cc` file, simply add the additional 526filename to the `sources` array: 527 528```json 529"sources": ["addon.cc", "myexample.cc"] 530``` 531 532Once the `binding.gyp` file is ready, the example addons can be configured and 533built using `node-gyp`: 534 535```console 536$ node-gyp configure build 537``` 538 539### Function arguments 540 541Addons will typically expose objects and functions that can be accessed from 542JavaScript running within Node.js. When functions are invoked from JavaScript, 543the input arguments and return value must be mapped to and from the C/C++ 544code. 545 546The following example illustrates how to read function arguments passed from 547JavaScript and how to return a result: 548 549```cpp 550// addon.cc 551#include <node.h> 552 553namespace demo { 554 555using v8::Exception; 556using v8::FunctionCallbackInfo; 557using v8::Isolate; 558using v8::Local; 559using v8::Number; 560using v8::Object; 561using v8::String; 562using v8::Value; 563 564// This is the implementation of the "add" method 565// Input arguments are passed using the 566// const FunctionCallbackInfo<Value>& args struct 567void Add(const FunctionCallbackInfo<Value>& args) { 568 Isolate* isolate = args.GetIsolate(); 569 570 // Check the number of arguments passed. 571 if (args.Length() < 2) { 572 // Throw an Error that is passed back to JavaScript 573 isolate->ThrowException(Exception::TypeError( 574 String::NewFromUtf8(isolate, 575 "Wrong number of arguments").ToLocalChecked())); 576 return; 577 } 578 579 // Check the argument types 580 if (!args[0]->IsNumber() || !args[1]->IsNumber()) { 581 isolate->ThrowException(Exception::TypeError( 582 String::NewFromUtf8(isolate, 583 "Wrong arguments").ToLocalChecked())); 584 return; 585 } 586 587 // Perform the operation 588 double value = 589 args[0].As<Number>()->Value() + args[1].As<Number>()->Value(); 590 Local<Number> num = Number::New(isolate, value); 591 592 // Set the return value (using the passed in 593 // FunctionCallbackInfo<Value>&) 594 args.GetReturnValue().Set(num); 595} 596 597void Init(Local<Object> exports) { 598 NODE_SET_METHOD(exports, "add", Add); 599} 600 601NODE_MODULE(NODE_GYP_MODULE_NAME, Init) 602 603} // namespace demo 604``` 605 606Once compiled, the example addon can be required and used from within Node.js: 607 608```js 609// test.js 610const addon = require('./build/Release/addon'); 611 612console.log('This should be eight:', addon.add(3, 5)); 613``` 614 615### Callbacks 616 617It is common practice within addons to pass JavaScript functions to a C++ 618function and execute them from there. The following example illustrates how 619to invoke such callbacks: 620 621```cpp 622// addon.cc 623#include <node.h> 624 625namespace demo { 626 627using v8::Context; 628using v8::Function; 629using v8::FunctionCallbackInfo; 630using v8::Isolate; 631using v8::Local; 632using v8::Null; 633using v8::Object; 634using v8::String; 635using v8::Value; 636 637void RunCallback(const FunctionCallbackInfo<Value>& args) { 638 Isolate* isolate = args.GetIsolate(); 639 Local<Context> context = isolate->GetCurrentContext(); 640 Local<Function> cb = Local<Function>::Cast(args[0]); 641 const unsigned argc = 1; 642 Local<Value> argv[argc] = { 643 String::NewFromUtf8(isolate, 644 "hello world").ToLocalChecked() }; 645 cb->Call(context, Null(isolate), argc, argv).ToLocalChecked(); 646} 647 648void Init(Local<Object> exports, Local<Object> module) { 649 NODE_SET_METHOD(module, "exports", RunCallback); 650} 651 652NODE_MODULE(NODE_GYP_MODULE_NAME, Init) 653 654} // namespace demo 655``` 656 657This example uses a two-argument form of `Init()` that receives the full 658`module` object as the second argument. This allows the addon to completely 659overwrite `exports` with a single function instead of adding the function as a 660property of `exports`. 661 662To test it, run the following JavaScript: 663 664```js 665// test.js 666const addon = require('./build/Release/addon'); 667 668addon((msg) => { 669 console.log(msg); 670// Prints: 'hello world' 671}); 672``` 673 674In this example, the callback function is invoked synchronously. 675 676### Object factory 677 678Addons can create and return new objects from within a C++ function as 679illustrated in the following example. An object is created and returned with a 680property `msg` that echoes the string passed to `createObject()`: 681 682```cpp 683// addon.cc 684#include <node.h> 685 686namespace demo { 687 688using v8::Context; 689using v8::FunctionCallbackInfo; 690using v8::Isolate; 691using v8::Local; 692using v8::Object; 693using v8::String; 694using v8::Value; 695 696void CreateObject(const FunctionCallbackInfo<Value>& args) { 697 Isolate* isolate = args.GetIsolate(); 698 Local<Context> context = isolate->GetCurrentContext(); 699 700 Local<Object> obj = Object::New(isolate); 701 obj->Set(context, 702 String::NewFromUtf8(isolate, 703 "msg").ToLocalChecked(), 704 args[0]->ToString(context).ToLocalChecked()) 705 .FromJust(); 706 707 args.GetReturnValue().Set(obj); 708} 709 710void Init(Local<Object> exports, Local<Object> module) { 711 NODE_SET_METHOD(module, "exports", CreateObject); 712} 713 714NODE_MODULE(NODE_GYP_MODULE_NAME, Init) 715 716} // namespace demo 717``` 718 719To test it in JavaScript: 720 721```js 722// test.js 723const addon = require('./build/Release/addon'); 724 725const obj1 = addon('hello'); 726const obj2 = addon('world'); 727console.log(obj1.msg, obj2.msg); 728// Prints: 'hello world' 729``` 730 731### Function factory 732 733Another common scenario is creating JavaScript functions that wrap C++ 734functions and returning those back to JavaScript: 735 736```cpp 737// addon.cc 738#include <node.h> 739 740namespace demo { 741 742using v8::Context; 743using v8::Function; 744using v8::FunctionCallbackInfo; 745using v8::FunctionTemplate; 746using v8::Isolate; 747using v8::Local; 748using v8::Object; 749using v8::String; 750using v8::Value; 751 752void MyFunction(const FunctionCallbackInfo<Value>& args) { 753 Isolate* isolate = args.GetIsolate(); 754 args.GetReturnValue().Set(String::NewFromUtf8( 755 isolate, "hello world").ToLocalChecked()); 756} 757 758void CreateFunction(const FunctionCallbackInfo<Value>& args) { 759 Isolate* isolate = args.GetIsolate(); 760 761 Local<Context> context = isolate->GetCurrentContext(); 762 Local<FunctionTemplate> tpl = FunctionTemplate::New(isolate, MyFunction); 763 Local<Function> fn = tpl->GetFunction(context).ToLocalChecked(); 764 765 // omit this to make it anonymous 766 fn->SetName(String::NewFromUtf8( 767 isolate, "theFunction").ToLocalChecked()); 768 769 args.GetReturnValue().Set(fn); 770} 771 772void Init(Local<Object> exports, Local<Object> module) { 773 NODE_SET_METHOD(module, "exports", CreateFunction); 774} 775 776NODE_MODULE(NODE_GYP_MODULE_NAME, Init) 777 778} // namespace demo 779``` 780 781To test: 782 783```js 784// test.js 785const addon = require('./build/Release/addon'); 786 787const fn = addon(); 788console.log(fn()); 789// Prints: 'hello world' 790``` 791 792### Wrapping C++ objects 793 794It is also possible to wrap C++ objects/classes in a way that allows new 795instances to be created using the JavaScript `new` operator: 796 797```cpp 798// addon.cc 799#include <node.h> 800#include "myobject.h" 801 802namespace demo { 803 804using v8::Local; 805using v8::Object; 806 807void InitAll(Local<Object> exports) { 808 MyObject::Init(exports); 809} 810 811NODE_MODULE(NODE_GYP_MODULE_NAME, InitAll) 812 813} // namespace demo 814``` 815 816Then, in `myobject.h`, the wrapper class inherits from `node::ObjectWrap`: 817 818```cpp 819// myobject.h 820#ifndef MYOBJECT_H 821#define MYOBJECT_H 822 823#include <node.h> 824#include <node_object_wrap.h> 825 826namespace demo { 827 828class MyObject : public node::ObjectWrap { 829 public: 830 static void Init(v8::Local<v8::Object> exports); 831 832 private: 833 explicit MyObject(double value = 0); 834 ~MyObject(); 835 836 static void New(const v8::FunctionCallbackInfo<v8::Value>& args); 837 static void PlusOne(const v8::FunctionCallbackInfo<v8::Value>& args); 838 839 double value_; 840}; 841 842} // namespace demo 843 844#endif 845``` 846 847In `myobject.cc`, implement the various methods that are to be exposed. 848Below, the method `plusOne()` is exposed by adding it to the constructor's 849prototype: 850 851```cpp 852// myobject.cc 853#include "myobject.h" 854 855namespace demo { 856 857using v8::Context; 858using v8::Function; 859using v8::FunctionCallbackInfo; 860using v8::FunctionTemplate; 861using v8::Isolate; 862using v8::Local; 863using v8::Number; 864using v8::Object; 865using v8::ObjectTemplate; 866using v8::String; 867using v8::Value; 868 869MyObject::MyObject(double value) : value_(value) { 870} 871 872MyObject::~MyObject() { 873} 874 875void MyObject::Init(Local<Object> exports) { 876 Isolate* isolate = exports->GetIsolate(); 877 Local<Context> context = isolate->GetCurrentContext(); 878 879 Local<ObjectTemplate> addon_data_tpl = ObjectTemplate::New(isolate); 880 addon_data_tpl->SetInternalFieldCount(1); // 1 field for the MyObject::New() 881 Local<Object> addon_data = 882 addon_data_tpl->NewInstance(context).ToLocalChecked(); 883 884 // Prepare constructor template 885 Local<FunctionTemplate> tpl = FunctionTemplate::New(isolate, New, addon_data); 886 tpl->SetClassName(String::NewFromUtf8(isolate, "MyObject").ToLocalChecked()); 887 tpl->InstanceTemplate()->SetInternalFieldCount(1); 888 889 // Prototype 890 NODE_SET_PROTOTYPE_METHOD(tpl, "plusOne", PlusOne); 891 892 Local<Function> constructor = tpl->GetFunction(context).ToLocalChecked(); 893 addon_data->SetInternalField(0, constructor); 894 exports->Set(context, String::NewFromUtf8( 895 isolate, "MyObject").ToLocalChecked(), 896 constructor).FromJust(); 897} 898 899void MyObject::New(const FunctionCallbackInfo<Value>& args) { 900 Isolate* isolate = args.GetIsolate(); 901 Local<Context> context = isolate->GetCurrentContext(); 902 903 if (args.IsConstructCall()) { 904 // Invoked as constructor: `new MyObject(...)` 905 double value = args[0]->IsUndefined() ? 906 0 : args[0]->NumberValue(context).FromMaybe(0); 907 MyObject* obj = new MyObject(value); 908 obj->Wrap(args.This()); 909 args.GetReturnValue().Set(args.This()); 910 } else { 911 // Invoked as plain function `MyObject(...)`, turn into construct call. 912 const int argc = 1; 913 Local<Value> argv[argc] = { args[0] }; 914 Local<Function> cons = 915 args.Data().As<Object>()->GetInternalField(0).As<Function>(); 916 Local<Object> result = 917 cons->NewInstance(context, argc, argv).ToLocalChecked(); 918 args.GetReturnValue().Set(result); 919 } 920} 921 922void MyObject::PlusOne(const FunctionCallbackInfo<Value>& args) { 923 Isolate* isolate = args.GetIsolate(); 924 925 MyObject* obj = ObjectWrap::Unwrap<MyObject>(args.Holder()); 926 obj->value_ += 1; 927 928 args.GetReturnValue().Set(Number::New(isolate, obj->value_)); 929} 930 931} // namespace demo 932``` 933 934To build this example, the `myobject.cc` file must be added to the 935`binding.gyp`: 936 937```json 938{ 939 "targets": [ 940 { 941 "target_name": "addon", 942 "sources": [ 943 "addon.cc", 944 "myobject.cc" 945 ] 946 } 947 ] 948} 949``` 950 951Test it with: 952 953```js 954// test.js 955const addon = require('./build/Release/addon'); 956 957const obj = new addon.MyObject(10); 958console.log(obj.plusOne()); 959// Prints: 11 960console.log(obj.plusOne()); 961// Prints: 12 962console.log(obj.plusOne()); 963// Prints: 13 964``` 965 966The destructor for a wrapper object will run when the object is 967garbage-collected. For destructor testing, there are command-line flags that 968can be used to make it possible to force garbage collection. These flags are 969provided by the underlying V8 JavaScript engine. They are subject to change 970or removal at any time. They are not documented by Node.js or V8, and they 971should never be used outside of testing. 972 973During shutdown of the process or worker threads destructors are not called 974by the JS engine. Therefore it's the responsibility of the user to track 975these objects and ensure proper destruction to avoid resource leaks. 976 977### Factory of wrapped objects 978 979Alternatively, it is possible to use a factory pattern to avoid explicitly 980creating object instances using the JavaScript `new` operator: 981 982```js 983const obj = addon.createObject(); 984// instead of: 985// const obj = new addon.Object(); 986``` 987 988First, the `createObject()` method is implemented in `addon.cc`: 989 990```cpp 991// addon.cc 992#include <node.h> 993#include "myobject.h" 994 995namespace demo { 996 997using v8::FunctionCallbackInfo; 998using v8::Isolate; 999using v8::Local; 1000using v8::Object; 1001using v8::String; 1002using v8::Value; 1003 1004void CreateObject(const FunctionCallbackInfo<Value>& args) { 1005 MyObject::NewInstance(args); 1006} 1007 1008void InitAll(Local<Object> exports, Local<Object> module) { 1009 MyObject::Init(exports->GetIsolate()); 1010 1011 NODE_SET_METHOD(module, "exports", CreateObject); 1012} 1013 1014NODE_MODULE(NODE_GYP_MODULE_NAME, InitAll) 1015 1016} // namespace demo 1017``` 1018 1019In `myobject.h`, the static method `NewInstance()` is added to handle 1020instantiating the object. This method takes the place of using `new` in 1021JavaScript: 1022 1023```cpp 1024// myobject.h 1025#ifndef MYOBJECT_H 1026#define MYOBJECT_H 1027 1028#include <node.h> 1029#include <node_object_wrap.h> 1030 1031namespace demo { 1032 1033class MyObject : public node::ObjectWrap { 1034 public: 1035 static void Init(v8::Isolate* isolate); 1036 static void NewInstance(const v8::FunctionCallbackInfo<v8::Value>& args); 1037 1038 private: 1039 explicit MyObject(double value = 0); 1040 ~MyObject(); 1041 1042 static void New(const v8::FunctionCallbackInfo<v8::Value>& args); 1043 static void PlusOne(const v8::FunctionCallbackInfo<v8::Value>& args); 1044 static v8::Global<v8::Function> constructor; 1045 double value_; 1046}; 1047 1048} // namespace demo 1049 1050#endif 1051``` 1052 1053The implementation in `myobject.cc` is similar to the previous example: 1054 1055```cpp 1056// myobject.cc 1057#include <node.h> 1058#include "myobject.h" 1059 1060namespace demo { 1061 1062using node::AddEnvironmentCleanupHook; 1063using v8::Context; 1064using v8::Function; 1065using v8::FunctionCallbackInfo; 1066using v8::FunctionTemplate; 1067using v8::Global; 1068using v8::Isolate; 1069using v8::Local; 1070using v8::Number; 1071using v8::Object; 1072using v8::String; 1073using v8::Value; 1074 1075// Warning! This is not thread-safe, this addon cannot be used for worker 1076// threads. 1077Global<Function> MyObject::constructor; 1078 1079MyObject::MyObject(double value) : value_(value) { 1080} 1081 1082MyObject::~MyObject() { 1083} 1084 1085void MyObject::Init(Isolate* isolate) { 1086 // Prepare constructor template 1087 Local<FunctionTemplate> tpl = FunctionTemplate::New(isolate, New); 1088 tpl->SetClassName(String::NewFromUtf8(isolate, "MyObject").ToLocalChecked()); 1089 tpl->InstanceTemplate()->SetInternalFieldCount(1); 1090 1091 // Prototype 1092 NODE_SET_PROTOTYPE_METHOD(tpl, "plusOne", PlusOne); 1093 1094 Local<Context> context = isolate->GetCurrentContext(); 1095 constructor.Reset(isolate, tpl->GetFunction(context).ToLocalChecked()); 1096 1097 AddEnvironmentCleanupHook(isolate, [](void*) { 1098 constructor.Reset(); 1099 }, nullptr); 1100} 1101 1102void MyObject::New(const FunctionCallbackInfo<Value>& args) { 1103 Isolate* isolate = args.GetIsolate(); 1104 Local<Context> context = isolate->GetCurrentContext(); 1105 1106 if (args.IsConstructCall()) { 1107 // Invoked as constructor: `new MyObject(...)` 1108 double value = args[0]->IsUndefined() ? 1109 0 : args[0]->NumberValue(context).FromMaybe(0); 1110 MyObject* obj = new MyObject(value); 1111 obj->Wrap(args.This()); 1112 args.GetReturnValue().Set(args.This()); 1113 } else { 1114 // Invoked as plain function `MyObject(...)`, turn into construct call. 1115 const int argc = 1; 1116 Local<Value> argv[argc] = { args[0] }; 1117 Local<Function> cons = Local<Function>::New(isolate, constructor); 1118 Local<Object> instance = 1119 cons->NewInstance(context, argc, argv).ToLocalChecked(); 1120 args.GetReturnValue().Set(instance); 1121 } 1122} 1123 1124void MyObject::NewInstance(const FunctionCallbackInfo<Value>& args) { 1125 Isolate* isolate = args.GetIsolate(); 1126 1127 const unsigned argc = 1; 1128 Local<Value> argv[argc] = { args[0] }; 1129 Local<Function> cons = Local<Function>::New(isolate, constructor); 1130 Local<Context> context = isolate->GetCurrentContext(); 1131 Local<Object> instance = 1132 cons->NewInstance(context, argc, argv).ToLocalChecked(); 1133 1134 args.GetReturnValue().Set(instance); 1135} 1136 1137void MyObject::PlusOne(const FunctionCallbackInfo<Value>& args) { 1138 Isolate* isolate = args.GetIsolate(); 1139 1140 MyObject* obj = ObjectWrap::Unwrap<MyObject>(args.Holder()); 1141 obj->value_ += 1; 1142 1143 args.GetReturnValue().Set(Number::New(isolate, obj->value_)); 1144} 1145 1146} // namespace demo 1147``` 1148 1149Once again, to build this example, the `myobject.cc` file must be added to the 1150`binding.gyp`: 1151 1152```json 1153{ 1154 "targets": [ 1155 { 1156 "target_name": "addon", 1157 "sources": [ 1158 "addon.cc", 1159 "myobject.cc" 1160 ] 1161 } 1162 ] 1163} 1164``` 1165 1166Test it with: 1167 1168```js 1169// test.js 1170const createObject = require('./build/Release/addon'); 1171 1172const obj = createObject(10); 1173console.log(obj.plusOne()); 1174// Prints: 11 1175console.log(obj.plusOne()); 1176// Prints: 12 1177console.log(obj.plusOne()); 1178// Prints: 13 1179 1180const obj2 = createObject(20); 1181console.log(obj2.plusOne()); 1182// Prints: 21 1183console.log(obj2.plusOne()); 1184// Prints: 22 1185console.log(obj2.plusOne()); 1186// Prints: 23 1187``` 1188 1189### Passing wrapped objects around 1190 1191In addition to wrapping and returning C++ objects, it is possible to pass 1192wrapped objects around by unwrapping them with the Node.js helper function 1193`node::ObjectWrap::Unwrap`. The following examples shows a function `add()` 1194that can take two `MyObject` objects as input arguments: 1195 1196```cpp 1197// addon.cc 1198#include <node.h> 1199#include <node_object_wrap.h> 1200#include "myobject.h" 1201 1202namespace demo { 1203 1204using v8::Context; 1205using v8::FunctionCallbackInfo; 1206using v8::Isolate; 1207using v8::Local; 1208using v8::Number; 1209using v8::Object; 1210using v8::String; 1211using v8::Value; 1212 1213void CreateObject(const FunctionCallbackInfo<Value>& args) { 1214 MyObject::NewInstance(args); 1215} 1216 1217void Add(const FunctionCallbackInfo<Value>& args) { 1218 Isolate* isolate = args.GetIsolate(); 1219 Local<Context> context = isolate->GetCurrentContext(); 1220 1221 MyObject* obj1 = node::ObjectWrap::Unwrap<MyObject>( 1222 args[0]->ToObject(context).ToLocalChecked()); 1223 MyObject* obj2 = node::ObjectWrap::Unwrap<MyObject>( 1224 args[1]->ToObject(context).ToLocalChecked()); 1225 1226 double sum = obj1->value() + obj2->value(); 1227 args.GetReturnValue().Set(Number::New(isolate, sum)); 1228} 1229 1230void InitAll(Local<Object> exports) { 1231 MyObject::Init(exports->GetIsolate()); 1232 1233 NODE_SET_METHOD(exports, "createObject", CreateObject); 1234 NODE_SET_METHOD(exports, "add", Add); 1235} 1236 1237NODE_MODULE(NODE_GYP_MODULE_NAME, InitAll) 1238 1239} // namespace demo 1240``` 1241 1242In `myobject.h`, a new public method is added to allow access to private values 1243after unwrapping the object. 1244 1245```cpp 1246// myobject.h 1247#ifndef MYOBJECT_H 1248#define MYOBJECT_H 1249 1250#include <node.h> 1251#include <node_object_wrap.h> 1252 1253namespace demo { 1254 1255class MyObject : public node::ObjectWrap { 1256 public: 1257 static void Init(v8::Isolate* isolate); 1258 static void NewInstance(const v8::FunctionCallbackInfo<v8::Value>& args); 1259 inline double value() const { return value_; } 1260 1261 private: 1262 explicit MyObject(double value = 0); 1263 ~MyObject(); 1264 1265 static void New(const v8::FunctionCallbackInfo<v8::Value>& args); 1266 static v8::Global<v8::Function> constructor; 1267 double value_; 1268}; 1269 1270} // namespace demo 1271 1272#endif 1273``` 1274 1275The implementation of `myobject.cc` is similar to before: 1276 1277```cpp 1278// myobject.cc 1279#include <node.h> 1280#include "myobject.h" 1281 1282namespace demo { 1283 1284using node::AddEnvironmentCleanupHook; 1285using v8::Context; 1286using v8::Function; 1287using v8::FunctionCallbackInfo; 1288using v8::FunctionTemplate; 1289using v8::Global; 1290using v8::Isolate; 1291using v8::Local; 1292using v8::Object; 1293using v8::String; 1294using v8::Value; 1295 1296// Warning! This is not thread-safe, this addon cannot be used for worker 1297// threads. 1298Global<Function> MyObject::constructor; 1299 1300MyObject::MyObject(double value) : value_(value) { 1301} 1302 1303MyObject::~MyObject() { 1304} 1305 1306void MyObject::Init(Isolate* isolate) { 1307 // Prepare constructor template 1308 Local<FunctionTemplate> tpl = FunctionTemplate::New(isolate, New); 1309 tpl->SetClassName(String::NewFromUtf8(isolate, "MyObject").ToLocalChecked()); 1310 tpl->InstanceTemplate()->SetInternalFieldCount(1); 1311 1312 Local<Context> context = isolate->GetCurrentContext(); 1313 constructor.Reset(isolate, tpl->GetFunction(context).ToLocalChecked()); 1314 1315 AddEnvironmentCleanupHook(isolate, [](void*) { 1316 constructor.Reset(); 1317 }, nullptr); 1318} 1319 1320void MyObject::New(const FunctionCallbackInfo<Value>& args) { 1321 Isolate* isolate = args.GetIsolate(); 1322 Local<Context> context = isolate->GetCurrentContext(); 1323 1324 if (args.IsConstructCall()) { 1325 // Invoked as constructor: `new MyObject(...)` 1326 double value = args[0]->IsUndefined() ? 1327 0 : args[0]->NumberValue(context).FromMaybe(0); 1328 MyObject* obj = new MyObject(value); 1329 obj->Wrap(args.This()); 1330 args.GetReturnValue().Set(args.This()); 1331 } else { 1332 // Invoked as plain function `MyObject(...)`, turn into construct call. 1333 const int argc = 1; 1334 Local<Value> argv[argc] = { args[0] }; 1335 Local<Function> cons = Local<Function>::New(isolate, constructor); 1336 Local<Object> instance = 1337 cons->NewInstance(context, argc, argv).ToLocalChecked(); 1338 args.GetReturnValue().Set(instance); 1339 } 1340} 1341 1342void MyObject::NewInstance(const FunctionCallbackInfo<Value>& args) { 1343 Isolate* isolate = args.GetIsolate(); 1344 1345 const unsigned argc = 1; 1346 Local<Value> argv[argc] = { args[0] }; 1347 Local<Function> cons = Local<Function>::New(isolate, constructor); 1348 Local<Context> context = isolate->GetCurrentContext(); 1349 Local<Object> instance = 1350 cons->NewInstance(context, argc, argv).ToLocalChecked(); 1351 1352 args.GetReturnValue().Set(instance); 1353} 1354 1355} // namespace demo 1356``` 1357 1358Test it with: 1359 1360```js 1361// test.js 1362const addon = require('./build/Release/addon'); 1363 1364const obj1 = addon.createObject(10); 1365const obj2 = addon.createObject(20); 1366const result = addon.add(obj1, obj2); 1367 1368console.log(result); 1369// Prints: 30 1370``` 1371 1372[Electron]: https://electronjs.org/ 1373[Embedder's Guide]: https://v8.dev/docs/embed 1374[Linking to libraries included with Node.js]: #linking-to-libraries-included-with-nodejs 1375[Native Abstractions for Node.js]: https://github.com/nodejs/nan 1376[V8]: https://v8.dev/ 1377[`Worker`]: worker_threads.md#class-worker 1378[bindings]: https://github.com/TooTallNate/node-bindings 1379[download]: https://github.com/nodejs/node-addon-examples 1380[examples]: https://github.com/nodejs/nan/tree/HEAD/examples/ 1381[implementation considerations]: n-api.md#implications-of-abi-stability 1382[installation instructions]: https://github.com/nodejs/node-gyp#installation 1383[libuv]: https://github.com/libuv/libuv 1384[node-gyp]: https://github.com/nodejs/node-gyp 1385[require]: modules.md#requireid 1386[v8-docs]: https://v8docs.nodesource.com/ 1387