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: N-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 N-API, use N-API. 13Refer to [C/C++ addons with N-API](n-api.html) for more information on N-API. 14 15When not using N-API, implementing addons is complicated, 16involving knowledge of several components and APIs: 17 18* V8: the C++ library Node.js uses to provide the 19 JavaScript implementation. V8 provides the mechanisms for creating objects, 20 calling functions, etc. V8's API is documented mostly in the 21 `v8.h` header file (`deps/v8/include/v8.h` in the Node.js source 22 tree), which is also available [online][v8-docs]. 23 24* [libuv][]: The C library that implements the Node.js event loop, its worker 25 threads and all of the asynchronous behaviors of the platform. It also 26 serves as a cross-platform abstraction library, giving easy, POSIX-like 27 access across all major operating systems to many common system tasks, such 28 as interacting with the filesystem, sockets, timers, and system events. libuv 29 also provides a pthreads-like threading abstraction that may be used to 30 power more sophisticated asynchronous addons that need to move beyond the 31 standard event loop. Addon authors are encouraged to think about how to 32 avoid blocking the event loop with I/O or other time-intensive tasks by 33 off-loading work via libuv to non-blocking system operations, worker threads 34 or a custom use of libuv's threads. 35 36* Internal Node.js libraries. Node.js itself exports C++ APIs that addons can 37 use, the most important of which is the `node::ObjectWrap` class. 38 39* Node.js includes other statically linked libraries including OpenSSL. These 40 other libraries are located in the `deps/` directory in the Node.js source 41 tree. Only the libuv, OpenSSL, V8 and zlib symbols are purposefully 42 re-exported by Node.js and may be used to various extents by addons. See 43 [Linking to libraries included with Node.js][] for additional information. 44 45All of the following examples are available for [download][] and may 46be used as the starting-point for an addon. 47 48## Hello world 49 50This "Hello world" example is a simple addon, written in C++, that is the 51equivalent of the following JavaScript code: 52 53```js 54module.exports.hello = () => 'world'; 55``` 56 57First, create the file `hello.cc`: 58 59```cpp 60// hello.cc 61#include <node.h> 62 63namespace demo { 64 65using v8::FunctionCallbackInfo; 66using v8::Isolate; 67using v8::Local; 68using v8::NewStringType; 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", NewStringType::kNormal).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()`. From the addon's perspective, this means 116that it must 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* Define a class which will hold per-addon-instance data and which has a static 159member of the form 160 ```cpp 161 static void DeleteInstance(void* data) { 162 // Cast `data` to an instance of the class and delete it. 163 } 164 ``` 165* Heap-allocate an instance of this class in the addon initializer. This can be 166accomplished using the `new` keyword. 167* Call `node::AddEnvironmentCleanupHook()`, passing it the above-created 168instance and a pointer to `DeleteInstance()`. This will ensure the instance is 169deleted when the environment is torn down. 170* Store the instance of the class in a `v8::External`, and 171* Pass the `v8::External` to all methods exposed to JavaScript by passing it 172to `v8::FunctionTemplate::New()` or `v8::Function::New()` which creates the 173native-backed JavaScript functions. The third parameter of 174`v8::FunctionTemplate::New()` or `v8::Function::New()` accepts the 175`v8::External` and makes it available in the native callback using the 176`v8::FunctionCallbackInfo::Data()` method. 177 178This will ensure that the per-addon-instance data reaches each binding that can 179be called from JavaScript. The per-addon-instance data must also be passed into 180any asynchronous callbacks the addon may create. 181 182The following example illustrates the implementation of a context-aware addon: 183 184```cpp 185#include <node.h> 186 187using namespace v8; 188 189class AddonData { 190 public: 191 explicit AddonData(Isolate* isolate): 192 call_count(0) { 193 // Ensure this per-addon-instance data is deleted at environment cleanup. 194 node::AddEnvironmentCleanupHook(isolate, DeleteInstance, this); 195 } 196 197 // Per-addon data. 198 int call_count; 199 200 static void DeleteInstance(void* data) { 201 delete static_cast<AddonData*>(data); 202 } 203}; 204 205static void Method(const v8::FunctionCallbackInfo<v8::Value>& info) { 206 // Retrieve the per-addon-instance data. 207 AddonData* data = 208 reinterpret_cast<AddonData*>(info.Data().As<External>()->Value()); 209 data->call_count++; 210 info.GetReturnValue().Set((double)data->call_count); 211} 212 213// Initialize this addon to be context-aware. 214NODE_MODULE_INIT(/* exports, module, context */) { 215 Isolate* isolate = context->GetIsolate(); 216 217 // Create a new instance of `AddonData` for this instance of the addon and 218 // tie its life cycle to that of the Node.js environment. 219 AddonData* data = new AddonData(isolate); 220 221 // Wrap the data in a `v8::External` so we can pass it to the method we 222 // expose. 223 Local<External> external = External::New(isolate, data); 224 225 // Expose the method `Method` to JavaScript, and make sure it receives the 226 // per-addon-instance data we created above by passing `external` as the 227 // third parameter to the `FunctionTemplate` constructor. 228 exports->Set(context, 229 String::NewFromUtf8(isolate, "method", NewStringType::kNormal) 230 .ToLocalChecked(), 231 FunctionTemplate::New(isolate, Method, external) 232 ->GetFunction(context).ToLocalChecked()).FromJust(); 233} 234``` 235 236#### Worker support 237<!-- YAML 238changes: 239 - version: v12.19.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 N-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 <assert.h> 275#include <stdlib.h> 276#include <node.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 400and download either the full source tarball or just the headers. If the full 401source is downloaded, addons will have complete access to the full set of 402Node.js dependencies. However, if only the Node.js headers are downloaded, then 403only 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 406source image. Using this option, the addon will have access to the full set of 407dependencies. 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## N-API 440 441> Stability: 2 - Stable 442 443N-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 N-API are used. 454 455Creating and maintaining an addon that benefits from the ABI stability 456provided by N-API carries with it certain 457[implementation considerations](n-api.html#n_api_implications_of_abi_stability). 458 459To use N-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 N-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 N-API](n-api.html). 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::NewStringType; 553using v8::Number; 554using v8::Object; 555using v8::String; 556using v8::Value; 557 558// This is the implementation of the "add" method 559// Input arguments are passed using the 560// const FunctionCallbackInfo<Value>& args struct 561void Add(const FunctionCallbackInfo<Value>& args) { 562 Isolate* isolate = args.GetIsolate(); 563 564 // Check the number of arguments passed. 565 if (args.Length() < 2) { 566 // Throw an Error that is passed back to JavaScript 567 isolate->ThrowException(Exception::TypeError( 568 String::NewFromUtf8(isolate, 569 "Wrong number of arguments", 570 NewStringType::kNormal).ToLocalChecked())); 571 return; 572 } 573 574 // Check the argument types 575 if (!args[0]->IsNumber() || !args[1]->IsNumber()) { 576 isolate->ThrowException(Exception::TypeError( 577 String::NewFromUtf8(isolate, 578 "Wrong arguments", 579 NewStringType::kNormal).ToLocalChecked())); 580 return; 581 } 582 583 // Perform the operation 584 double value = 585 args[0].As<Number>()->Value() + args[1].As<Number>()->Value(); 586 Local<Number> num = Number::New(isolate, value); 587 588 // Set the return value (using the passed in 589 // FunctionCallbackInfo<Value>&) 590 args.GetReturnValue().Set(num); 591} 592 593void Init(Local<Object> exports) { 594 NODE_SET_METHOD(exports, "add", Add); 595} 596 597NODE_MODULE(NODE_GYP_MODULE_NAME, Init) 598 599} // namespace demo 600``` 601 602Once compiled, the example addon can be required and used from within Node.js: 603 604```js 605// test.js 606const addon = require('./build/Release/addon'); 607 608console.log('This should be eight:', addon.add(3, 5)); 609``` 610 611### Callbacks 612 613It is common practice within addons to pass JavaScript functions to a C++ 614function and execute them from there. The following example illustrates how 615to invoke such callbacks: 616 617```cpp 618// addon.cc 619#include <node.h> 620 621namespace demo { 622 623using v8::Context; 624using v8::Function; 625using v8::FunctionCallbackInfo; 626using v8::Isolate; 627using v8::Local; 628using v8::NewStringType; 629using v8::Null; 630using v8::Object; 631using v8::String; 632using v8::Value; 633 634void RunCallback(const FunctionCallbackInfo<Value>& args) { 635 Isolate* isolate = args.GetIsolate(); 636 Local<Context> context = isolate->GetCurrentContext(); 637 Local<Function> cb = Local<Function>::Cast(args[0]); 638 const unsigned argc = 1; 639 Local<Value> argv[argc] = { 640 String::NewFromUtf8(isolate, 641 "hello world", 642 NewStringType::kNormal).ToLocalChecked() }; 643 cb->Call(context, Null(isolate), argc, argv).ToLocalChecked(); 644} 645 646void Init(Local<Object> exports, Local<Object> module) { 647 NODE_SET_METHOD(module, "exports", RunCallback); 648} 649 650NODE_MODULE(NODE_GYP_MODULE_NAME, Init) 651 652} // namespace demo 653``` 654 655This example uses a two-argument form of `Init()` that receives the full 656`module` object as the second argument. This allows the addon to completely 657overwrite `exports` with a single function instead of adding the function as a 658property of `exports`. 659 660To test it, run the following JavaScript: 661 662```js 663// test.js 664const addon = require('./build/Release/addon'); 665 666addon((msg) => { 667 console.log(msg); 668// Prints: 'hello world' 669}); 670``` 671 672In this example, the callback function is invoked synchronously. 673 674### Object factory 675 676Addons can create and return new objects from within a C++ function as 677illustrated in the following example. An object is created and returned with a 678property `msg` that echoes the string passed to `createObject()`: 679 680```cpp 681// addon.cc 682#include <node.h> 683 684namespace demo { 685 686using v8::Context; 687using v8::FunctionCallbackInfo; 688using v8::Isolate; 689using v8::Local; 690using v8::NewStringType; 691using v8::Object; 692using v8::String; 693using v8::Value; 694 695void CreateObject(const FunctionCallbackInfo<Value>& args) { 696 Isolate* isolate = args.GetIsolate(); 697 Local<Context> context = isolate->GetCurrentContext(); 698 699 Local<Object> obj = Object::New(isolate); 700 obj->Set(context, 701 String::NewFromUtf8(isolate, 702 "msg", 703 NewStringType::kNormal).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::NewStringType; 749using v8::Object; 750using v8::String; 751using v8::Value; 752 753void MyFunction(const FunctionCallbackInfo<Value>& args) { 754 Isolate* isolate = args.GetIsolate(); 755 args.GetReturnValue().Set(String::NewFromUtf8( 756 isolate, "hello world", NewStringType::kNormal).ToLocalChecked()); 757} 758 759void CreateFunction(const FunctionCallbackInfo<Value>& args) { 760 Isolate* isolate = args.GetIsolate(); 761 762 Local<Context> context = isolate->GetCurrentContext(); 763 Local<FunctionTemplate> tpl = FunctionTemplate::New(isolate, MyFunction); 764 Local<Function> fn = tpl->GetFunction(context).ToLocalChecked(); 765 766 // omit this to make it anonymous 767 fn->SetName(String::NewFromUtf8( 768 isolate, "theFunction", NewStringType::kNormal).ToLocalChecked()); 769 770 args.GetReturnValue().Set(fn); 771} 772 773void Init(Local<Object> exports, Local<Object> module) { 774 NODE_SET_METHOD(module, "exports", CreateFunction); 775} 776 777NODE_MODULE(NODE_GYP_MODULE_NAME, Init) 778 779} // namespace demo 780``` 781 782To test: 783 784```js 785// test.js 786const addon = require('./build/Release/addon'); 787 788const fn = addon(); 789console.log(fn()); 790// Prints: 'hello world' 791``` 792 793### Wrapping C++ objects 794 795It is also possible to wrap C++ objects/classes in a way that allows new 796instances to be created using the JavaScript `new` operator: 797 798```cpp 799// addon.cc 800#include <node.h> 801#include "myobject.h" 802 803namespace demo { 804 805using v8::Local; 806using v8::Object; 807 808void InitAll(Local<Object> exports) { 809 MyObject::Init(exports); 810} 811 812NODE_MODULE(NODE_GYP_MODULE_NAME, InitAll) 813 814} // namespace demo 815``` 816 817Then, in `myobject.h`, the wrapper class inherits from `node::ObjectWrap`: 818 819```cpp 820// myobject.h 821#ifndef MYOBJECT_H 822#define MYOBJECT_H 823 824#include <node.h> 825#include <node_object_wrap.h> 826 827namespace demo { 828 829class MyObject : public node::ObjectWrap { 830 public: 831 static void Init(v8::Local<v8::Object> exports); 832 833 private: 834 explicit MyObject(double value = 0); 835 ~MyObject(); 836 837 static void New(const v8::FunctionCallbackInfo<v8::Value>& args); 838 static void PlusOne(const v8::FunctionCallbackInfo<v8::Value>& args); 839 840 double value_; 841}; 842 843} // namespace demo 844 845#endif 846``` 847 848In `myobject.cc`, implement the various methods that are to be exposed. 849Below, the method `plusOne()` is exposed by adding it to the constructor's 850prototype: 851 852```cpp 853// myobject.cc 854#include "myobject.h" 855 856namespace demo { 857 858using v8::Context; 859using v8::Function; 860using v8::FunctionCallbackInfo; 861using v8::FunctionTemplate; 862using v8::Isolate; 863using v8::Local; 864using v8::NewStringType; 865using v8::Number; 866using v8::Object; 867using v8::ObjectTemplate; 868using v8::String; 869using v8::Value; 870 871MyObject::MyObject(double value) : value_(value) { 872} 873 874MyObject::~MyObject() { 875} 876 877void MyObject::Init(Local<Object> exports) { 878 Isolate* isolate = exports->GetIsolate(); 879 Local<Context> context = isolate->GetCurrentContext(); 880 881 Local<ObjectTemplate> addon_data_tpl = ObjectTemplate::New(isolate); 882 addon_data_tpl->SetInternalFieldCount(1); // 1 field for the MyObject::New() 883 Local<Object> addon_data = 884 addon_data_tpl->NewInstance(context).ToLocalChecked(); 885 886 // Prepare constructor template 887 Local<FunctionTemplate> tpl = FunctionTemplate::New(isolate, New, addon_data); 888 tpl->SetClassName(String::NewFromUtf8( 889 isolate, "MyObject", NewStringType::kNormal).ToLocalChecked()); 890 tpl->InstanceTemplate()->SetInternalFieldCount(1); 891 892 // Prototype 893 NODE_SET_PROTOTYPE_METHOD(tpl, "plusOne", PlusOne); 894 895 Local<Function> constructor = tpl->GetFunction(context).ToLocalChecked(); 896 addon_data->SetInternalField(0, constructor); 897 exports->Set(context, String::NewFromUtf8( 898 isolate, "MyObject", NewStringType::kNormal).ToLocalChecked(), 899 constructor).FromJust(); 900} 901 902void MyObject::New(const FunctionCallbackInfo<Value>& args) { 903 Isolate* isolate = args.GetIsolate(); 904 Local<Context> context = isolate->GetCurrentContext(); 905 906 if (args.IsConstructCall()) { 907 // Invoked as constructor: `new MyObject(...)` 908 double value = args[0]->IsUndefined() ? 909 0 : args[0]->NumberValue(context).FromMaybe(0); 910 MyObject* obj = new MyObject(value); 911 obj->Wrap(args.This()); 912 args.GetReturnValue().Set(args.This()); 913 } else { 914 // Invoked as plain function `MyObject(...)`, turn into construct call. 915 const int argc = 1; 916 Local<Value> argv[argc] = { args[0] }; 917 Local<Function> cons = 918 args.Data().As<Object>()->GetInternalField(0).As<Function>(); 919 Local<Object> result = 920 cons->NewInstance(context, argc, argv).ToLocalChecked(); 921 args.GetReturnValue().Set(result); 922 } 923} 924 925void MyObject::PlusOne(const FunctionCallbackInfo<Value>& args) { 926 Isolate* isolate = args.GetIsolate(); 927 928 MyObject* obj = ObjectWrap::Unwrap<MyObject>(args.Holder()); 929 obj->value_ += 1; 930 931 args.GetReturnValue().Set(Number::New(isolate, obj->value_)); 932} 933 934} // namespace demo 935``` 936 937To build this example, the `myobject.cc` file must be added to the 938`binding.gyp`: 939 940```json 941{ 942 "targets": [ 943 { 944 "target_name": "addon", 945 "sources": [ 946 "addon.cc", 947 "myobject.cc" 948 ] 949 } 950 ] 951} 952``` 953 954Test it with: 955 956```js 957// test.js 958const addon = require('./build/Release/addon'); 959 960const obj = new addon.MyObject(10); 961console.log(obj.plusOne()); 962// Prints: 11 963console.log(obj.plusOne()); 964// Prints: 12 965console.log(obj.plusOne()); 966// Prints: 13 967``` 968 969The destructor for a wrapper object will run when the object is 970garbage-collected. For destructor testing, there are command-line flags that 971can be used to make it possible to force garbage collection. These flags are 972provided by the underlying V8 JavaScript engine. They are subject to change 973or removal at any time. They are not documented by Node.js or V8, and they 974should never be used outside of testing. 975 976### Factory of wrapped objects 977 978Alternatively, it is possible to use a factory pattern to avoid explicitly 979creating object instances using the JavaScript `new` operator: 980 981```js 982const obj = addon.createObject(); 983// instead of: 984// const obj = new addon.Object(); 985``` 986 987First, the `createObject()` method is implemented in `addon.cc`: 988 989```cpp 990// addon.cc 991#include <node.h> 992#include "myobject.h" 993 994namespace demo { 995 996using v8::FunctionCallbackInfo; 997using v8::Isolate; 998using v8::Local; 999using v8::Object; 1000using v8::String; 1001using v8::Value; 1002 1003void CreateObject(const FunctionCallbackInfo<Value>& args) { 1004 MyObject::NewInstance(args); 1005} 1006 1007void InitAll(Local<Object> exports, Local<Object> module) { 1008 MyObject::Init(exports->GetIsolate()); 1009 1010 NODE_SET_METHOD(module, "exports", CreateObject); 1011} 1012 1013NODE_MODULE(NODE_GYP_MODULE_NAME, InitAll) 1014 1015} // namespace demo 1016``` 1017 1018In `myobject.h`, the static method `NewInstance()` is added to handle 1019instantiating the object. This method takes the place of using `new` in 1020JavaScript: 1021 1022```cpp 1023// myobject.h 1024#ifndef MYOBJECT_H 1025#define MYOBJECT_H 1026 1027#include <node.h> 1028#include <node_object_wrap.h> 1029 1030namespace demo { 1031 1032class MyObject : public node::ObjectWrap { 1033 public: 1034 static void Init(v8::Isolate* isolate); 1035 static void NewInstance(const v8::FunctionCallbackInfo<v8::Value>& args); 1036 1037 private: 1038 explicit MyObject(double value = 0); 1039 ~MyObject(); 1040 1041 static void New(const v8::FunctionCallbackInfo<v8::Value>& args); 1042 static void PlusOne(const v8::FunctionCallbackInfo<v8::Value>& args); 1043 static v8::Global<v8::Function> constructor; 1044 double value_; 1045}; 1046 1047} // namespace demo 1048 1049#endif 1050``` 1051 1052The implementation in `myobject.cc` is similar to the previous example: 1053 1054```cpp 1055// myobject.cc 1056#include <node.h> 1057#include "myobject.h" 1058 1059namespace demo { 1060 1061using node::AddEnvironmentCleanupHook; 1062using v8::Context; 1063using v8::Function; 1064using v8::FunctionCallbackInfo; 1065using v8::FunctionTemplate; 1066using v8::Global; 1067using v8::Isolate; 1068using v8::Local; 1069using v8::NewStringType; 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( 1089 isolate, "MyObject", NewStringType::kNormal).ToLocalChecked()); 1090 tpl->InstanceTemplate()->SetInternalFieldCount(1); 1091 1092 // Prototype 1093 NODE_SET_PROTOTYPE_METHOD(tpl, "plusOne", PlusOne); 1094 1095 Local<Context> context = isolate->GetCurrentContext(); 1096 constructor.Reset(isolate, tpl->GetFunction(context).ToLocalChecked()); 1097 1098 AddEnvironmentCleanupHook(isolate, [](void*) { 1099 constructor.Reset(); 1100 }, nullptr); 1101} 1102 1103void MyObject::New(const FunctionCallbackInfo<Value>& args) { 1104 Isolate* isolate = args.GetIsolate(); 1105 Local<Context> context = isolate->GetCurrentContext(); 1106 1107 if (args.IsConstructCall()) { 1108 // Invoked as constructor: `new MyObject(...)` 1109 double value = args[0]->IsUndefined() ? 1110 0 : args[0]->NumberValue(context).FromMaybe(0); 1111 MyObject* obj = new MyObject(value); 1112 obj->Wrap(args.This()); 1113 args.GetReturnValue().Set(args.This()); 1114 } else { 1115 // Invoked as plain function `MyObject(...)`, turn into construct call. 1116 const int argc = 1; 1117 Local<Value> argv[argc] = { args[0] }; 1118 Local<Function> cons = Local<Function>::New(isolate, constructor); 1119 Local<Object> instance = 1120 cons->NewInstance(context, argc, argv).ToLocalChecked(); 1121 args.GetReturnValue().Set(instance); 1122 } 1123} 1124 1125void MyObject::NewInstance(const FunctionCallbackInfo<Value>& args) { 1126 Isolate* isolate = args.GetIsolate(); 1127 1128 const unsigned argc = 1; 1129 Local<Value> argv[argc] = { args[0] }; 1130 Local<Function> cons = Local<Function>::New(isolate, constructor); 1131 Local<Context> context = isolate->GetCurrentContext(); 1132 Local<Object> instance = 1133 cons->NewInstance(context, argc, argv).ToLocalChecked(); 1134 1135 args.GetReturnValue().Set(instance); 1136} 1137 1138void MyObject::PlusOne(const FunctionCallbackInfo<Value>& args) { 1139 Isolate* isolate = args.GetIsolate(); 1140 1141 MyObject* obj = ObjectWrap::Unwrap<MyObject>(args.Holder()); 1142 obj->value_ += 1; 1143 1144 args.GetReturnValue().Set(Number::New(isolate, obj->value_)); 1145} 1146 1147} // namespace demo 1148``` 1149 1150Once again, to build this example, the `myobject.cc` file must be added to the 1151`binding.gyp`: 1152 1153```json 1154{ 1155 "targets": [ 1156 { 1157 "target_name": "addon", 1158 "sources": [ 1159 "addon.cc", 1160 "myobject.cc" 1161 ] 1162 } 1163 ] 1164} 1165``` 1166 1167Test it with: 1168 1169```js 1170// test.js 1171const createObject = require('./build/Release/addon'); 1172 1173const obj = createObject(10); 1174console.log(obj.plusOne()); 1175// Prints: 11 1176console.log(obj.plusOne()); 1177// Prints: 12 1178console.log(obj.plusOne()); 1179// Prints: 13 1180 1181const obj2 = createObject(20); 1182console.log(obj2.plusOne()); 1183// Prints: 21 1184console.log(obj2.plusOne()); 1185// Prints: 22 1186console.log(obj2.plusOne()); 1187// Prints: 23 1188``` 1189 1190### Passing wrapped objects around 1191 1192In addition to wrapping and returning C++ objects, it is possible to pass 1193wrapped objects around by unwrapping them with the Node.js helper function 1194`node::ObjectWrap::Unwrap`. The following examples shows a function `add()` 1195that can take two `MyObject` objects as input arguments: 1196 1197```cpp 1198// addon.cc 1199#include <node.h> 1200#include <node_object_wrap.h> 1201#include "myobject.h" 1202 1203namespace demo { 1204 1205using v8::Context; 1206using v8::FunctionCallbackInfo; 1207using v8::Isolate; 1208using v8::Local; 1209using v8::Number; 1210using v8::Object; 1211using v8::String; 1212using v8::Value; 1213 1214void CreateObject(const FunctionCallbackInfo<Value>& args) { 1215 MyObject::NewInstance(args); 1216} 1217 1218void Add(const FunctionCallbackInfo<Value>& args) { 1219 Isolate* isolate = args.GetIsolate(); 1220 Local<Context> context = isolate->GetCurrentContext(); 1221 1222 MyObject* obj1 = node::ObjectWrap::Unwrap<MyObject>( 1223 args[0]->ToObject(context).ToLocalChecked()); 1224 MyObject* obj2 = node::ObjectWrap::Unwrap<MyObject>( 1225 args[1]->ToObject(context).ToLocalChecked()); 1226 1227 double sum = obj1->value() + obj2->value(); 1228 args.GetReturnValue().Set(Number::New(isolate, sum)); 1229} 1230 1231void InitAll(Local<Object> exports) { 1232 MyObject::Init(exports->GetIsolate()); 1233 1234 NODE_SET_METHOD(exports, "createObject", CreateObject); 1235 NODE_SET_METHOD(exports, "add", Add); 1236} 1237 1238NODE_MODULE(NODE_GYP_MODULE_NAME, InitAll) 1239 1240} // namespace demo 1241``` 1242 1243In `myobject.h`, a new public method is added to allow access to private values 1244after unwrapping the object. 1245 1246```cpp 1247// myobject.h 1248#ifndef MYOBJECT_H 1249#define MYOBJECT_H 1250 1251#include <node.h> 1252#include <node_object_wrap.h> 1253 1254namespace demo { 1255 1256class MyObject : public node::ObjectWrap { 1257 public: 1258 static void Init(v8::Isolate* isolate); 1259 static void NewInstance(const v8::FunctionCallbackInfo<v8::Value>& args); 1260 inline double value() const { return value_; } 1261 1262 private: 1263 explicit MyObject(double value = 0); 1264 ~MyObject(); 1265 1266 static void New(const v8::FunctionCallbackInfo<v8::Value>& args); 1267 static v8::Global<v8::Function> constructor; 1268 double value_; 1269}; 1270 1271} // namespace demo 1272 1273#endif 1274``` 1275 1276The implementation of `myobject.cc` is similar to before: 1277 1278```cpp 1279// myobject.cc 1280#include <node.h> 1281#include "myobject.h" 1282 1283namespace demo { 1284 1285using node::AddEnvironmentCleanupHook; 1286using v8::Context; 1287using v8::Function; 1288using v8::FunctionCallbackInfo; 1289using v8::FunctionTemplate; 1290using v8::Global; 1291using v8::Isolate; 1292using v8::Local; 1293using v8::NewStringType; 1294using v8::Object; 1295using v8::String; 1296using v8::Value; 1297 1298// Warning! This is not thread-safe, this addon cannot be used for worker 1299// threads. 1300Global<Function> MyObject::constructor; 1301 1302MyObject::MyObject(double value) : value_(value) { 1303} 1304 1305MyObject::~MyObject() { 1306} 1307 1308void MyObject::Init(Isolate* isolate) { 1309 // Prepare constructor template 1310 Local<FunctionTemplate> tpl = FunctionTemplate::New(isolate, New); 1311 tpl->SetClassName(String::NewFromUtf8( 1312 isolate, "MyObject", NewStringType::kNormal).ToLocalChecked()); 1313 tpl->InstanceTemplate()->SetInternalFieldCount(1); 1314 1315 Local<Context> context = isolate->GetCurrentContext(); 1316 constructor.Reset(isolate, tpl->GetFunction(context).ToLocalChecked()); 1317 1318 AddEnvironmentCleanupHook(isolate, [](void*) { 1319 constructor.Reset(); 1320 }, nullptr); 1321} 1322 1323void MyObject::New(const FunctionCallbackInfo<Value>& args) { 1324 Isolate* isolate = args.GetIsolate(); 1325 Local<Context> context = isolate->GetCurrentContext(); 1326 1327 if (args.IsConstructCall()) { 1328 // Invoked as constructor: `new MyObject(...)` 1329 double value = args[0]->IsUndefined() ? 1330 0 : args[0]->NumberValue(context).FromMaybe(0); 1331 MyObject* obj = new MyObject(value); 1332 obj->Wrap(args.This()); 1333 args.GetReturnValue().Set(args.This()); 1334 } else { 1335 // Invoked as plain function `MyObject(...)`, turn into construct call. 1336 const int argc = 1; 1337 Local<Value> argv[argc] = { args[0] }; 1338 Local<Function> cons = Local<Function>::New(isolate, constructor); 1339 Local<Object> instance = 1340 cons->NewInstance(context, argc, argv).ToLocalChecked(); 1341 args.GetReturnValue().Set(instance); 1342 } 1343} 1344 1345void MyObject::NewInstance(const FunctionCallbackInfo<Value>& args) { 1346 Isolate* isolate = args.GetIsolate(); 1347 1348 const unsigned argc = 1; 1349 Local<Value> argv[argc] = { args[0] }; 1350 Local<Function> cons = Local<Function>::New(isolate, constructor); 1351 Local<Context> context = isolate->GetCurrentContext(); 1352 Local<Object> instance = 1353 cons->NewInstance(context, argc, argv).ToLocalChecked(); 1354 1355 args.GetReturnValue().Set(instance); 1356} 1357 1358} // namespace demo 1359``` 1360 1361Test it with: 1362 1363```js 1364// test.js 1365const addon = require('./build/Release/addon'); 1366 1367const obj1 = addon.createObject(10); 1368const obj2 = addon.createObject(20); 1369const result = addon.add(obj1, obj2); 1370 1371console.log(result); 1372// Prints: 30 1373``` 1374 1375[`Worker`]: worker_threads.html#worker_threads_class_worker 1376[Electron]: https://electronjs.org/ 1377[Embedder's Guide]: https://github.com/v8/v8/wiki/Embedder's%20Guide 1378[Linking to libraries included with Node.js]: #addons_linking_to_libraries_included_with_node_js 1379[Native Abstractions for Node.js]: https://github.com/nodejs/nan 1380[bindings]: https://github.com/TooTallNate/node-bindings 1381[download]: https://github.com/nodejs/node-addon-examples 1382[examples]: https://github.com/nodejs/nan/tree/master/examples/ 1383[installation instructions]: https://github.com/nodejs/node-gyp#installation 1384[libuv]: https://github.com/libuv/libuv 1385[node-gyp]: https://github.com/nodejs/node-gyp 1386[require]: modules.html#modules_require_id 1387[v8-docs]: https://v8docs.nodesource.com/ 1388