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