| # Addons |
| |
| Node.js Addons are dynamically-linked shared objects, written in C or C++, that |
| can be loaded into Node.js using the `require()` function, and used just as if |
| they were an ordinary Node.js module. They are used primarily to provide an |
| interface between JavaScript running in Node.js and C/C++ libraries. |
| |
| At the moment, the method for implementing Addons is rather complicated, |
| involving knowledge of several components and APIs : |
| |
| - V8: the C++ library Node.js currently uses to provide the |
| JavaScript implementation. V8 provides the mechanisms for creating objects, |
| calling functions, etc. V8's API is documented mostly in the |
| `v8.h` header file (`deps/v8/include/v8.h` in the Node.js source |
| tree), which is also available [online][]. |
| |
| - [libuv][]: The C library that implements the Node.js event loop, its worker |
| threads and all of the asynchronous behaviors of the platform. It also |
| serves as a cross-platform abstraction library, giving easy, POSIX-like |
| access across all major operating systems to many common system tasks, such |
| as interacting with the filesystem, sockets, timers and system events. libuv |
| also provides a pthreads-like threading abstraction that may be used to |
| power more sophisticated asynchronous Addons that need to move beyond the |
| standard event loop. Addon authors are encouraged to think about how to |
| avoid blocking the event loop with I/O or other time-intensive tasks by |
| off-loading work via libuv to non-blocking system operations, worker threads |
| or a custom use of libuv's threads. |
| |
| - Internal Node.js libraries. Node.js itself exports a number of C/C++ APIs |
| that Addons can use — the most important of which is the |
| `node::ObjectWrap` class. |
| |
| - Node.js includes a number of other statically linked libraries including |
| OpenSSL. These other libraries are located in the `deps/` directory in the |
| Node.js source tree. Only the V8 and OpenSSL symbols are purposefully |
| re-exported by Node.js and may be used to various extents by Addons. |
| See [Linking to Node.js' own dependencies][] for additional information. |
| |
| All of the following examples are available for [download][] and may |
| be used as a starting-point for your own Addon. |
| |
| ## Hello world |
| |
| This "Hello world" example is a simple Addon, written in C++, that is the |
| equivalent of the following JavaScript code: |
| |
| module.exports.hello = function() { return 'world'; }; |
| |
| First, create the file `hello.cc`: |
| |
| // hello.cc |
| #include <node.h> |
| |
| namespace demo { |
| |
| using v8::FunctionCallbackInfo; |
| using v8::Isolate; |
| using v8::Local; |
| using v8::Object; |
| using v8::String; |
| using v8::Value; |
| |
| void Method(const FunctionCallbackInfo<Value>& args) { |
| Isolate* isolate = args.GetIsolate(); |
| args.GetReturnValue().Set(String::NewFromUtf8(isolate, "world")); |
| } |
| |
| void init(Local<Object> exports) { |
| NODE_SET_METHOD(exports, "hello", Method); |
| } |
| |
| NODE_MODULE(addon, init) |
| |
| } // namespace demo |
| |
| Note that all Node.js Addons must export an initialization function following |
| the pattern: |
| |
| void Initialize(Local<Object> exports); |
| NODE_MODULE(module_name, Initialize) |
| |
| There is no semi-colon after `NODE_MODULE` as it's not a function (see |
| `node.h`). |
| |
| The `module_name` must match the filename of the final binary (excluding |
| the .node suffix). |
| |
| In the `hello.cc` example, then, the initialization function is `init` and the |
| Addon module name is `addon`. |
| |
| ### Building |
| |
| Once the source code has been written, it must be compiled into the binary |
| `addon.node` file. To do so, create a file called `binding.gyp` in the |
| top-level of the project describing the build configuration of your module |
| using a JSON-like format. This file is used by [node-gyp][] -- a tool written |
| specifically to compile Node.js Addons. |
| |
| { |
| "targets": [ |
| { |
| "target_name": "addon", |
| "sources": [ "hello.cc" ] |
| } |
| ] |
| } |
| |
| *Note: A version of the `node-gyp` utility is bundled and distributed with |
| Node.js as part of `npm`. This version is not made directly available for |
| developers to use and is intended only to support the ability to use the |
| `npm install` command to compile and install Addons. Developers who wish to |
| use `node-gyp` directly can install it using the command |
| `npm install -g node-gyp`. See the `node-gyp` [installation instructions] for |
| more information, including platform-specific requirements.* |
| |
| Once the `binding.gyp` file has been created, use `node-gyp configure` to |
| generate the appropriate project build files for the current platform. This |
| will generate either a `Makefile` (on Unix platforms) or a `vcxproj` file |
| (on Windows) in the `build/` directory. |
| |
| Next, invoke the `node-gyp build` command to generate the compiled `addon.node` |
| file. This will be put into the `build/Release/` directory. |
| |
| When using `npm install` to install a Node.js Addon, npm uses its own bundled |
| version of `node-gyp` to perform this same set of actions, generating a |
| compiled version of the Addon for the user's platform on demand. |
| |
| Once built, the binary Addon can be used from within Node.js by pointing |
| `require()` to the built `addon.node` module: |
| |
| // hello.js |
| const addon = require('./build/Release/addon'); |
| |
| console.log(addon.hello()); // 'world' |
| |
| Please see the examples below for further information or |
| <https://github.com/arturadib/node-qt> for an example in production. |
| |
| Because the exact path to the compiled Addon binary can vary depending on how |
| it is compiled (i.e. sometimes it may be in `./build/Debug/`), Addons can use |
| the [bindings][] package to load the compiled module. |
| |
| Note that while the `bindings` package implementation is more sophisticated |
| in how it locates Addon modules, it is essentially using a try-catch pattern |
| similar to: |
| |
| try { |
| return require('./build/Release/addon.node'); |
| } catch (err) { |
| return require('./build/Debug/addon.node'); |
| } |
| |
| ### Linking to Node.js' own dependencies |
| |
| Node.js uses a number of statically linked libraries such as V8, libuv and |
| OpenSSL. All Addons are required to link to V8 and may link to any of the |
| other dependencies as well. Typically, this is as simple as including |
| the appropriate `#include <...>` statements (e.g. `#include <v8.h>`) and |
| `node-gyp` will locate the appropriate headers automatically. However, there |
| are a few caveats to be aware of: |
| |
| * When `node-gyp` runs, it will detect the specific release version of Node.js |
| and download either the full source tarball or just the headers. If the full |
| source is downloaded, Addons will have complete access to the full set of |
| Node.js dependencies. However, if only the Node.js headers are downloaded, then |
| only the symbols exported by Node.js will be available. |
| |
| * `node-gyp` can be run using the `--nodedir` flag pointing at a local Node.js |
| source image. Using this option, the Addon will have access to the full set of |
| dependencies. |
| |
| ### Loading Addons using require() |
| |
| The filename extension of the compiled Addon binary is `.node` (as opposed |
| to `.dll` or `.so`). The `require()` function is written to look for files |
| with the `.node` file extension and initialize those as dynamically-linked |
| libraries. |
| |
| When calling `require()`, the `.node` extension can usually be |
| omitted and Node.js will still find and initialize the Addon. One caveat, |
| however, is that Node.js will first attempt to locate and load modules or |
| JavaScript files that happen to share the same base name. For instance, if |
| there is a file `addon.js` in the same directory as the binary `addon.node`, |
| then `require('addon')` will give precedence to the `addon.js` file and load it |
| instead. |
| |
| ## Native Abstractions for Node.js |
| |
| Each of the examples illustrated in this document make direct use of the |
| Node.js and V8 APIs for implementing Addons. It is important to understand |
| that the V8 API can, and has, changed dramatically from one V8 release to the |
| next (and one major Node.js release to the next). With each change, Addons may |
| need to be updated and recompiled in order to continue functioning. The Node.js |
| release schedule is designed to minimize the frequency and impact of such |
| changes but there is little that Node.js can do currently to ensure stability |
| of the V8 APIs. |
| |
| The [Native Abstrations for Node.js][] (or `nan`) provide a set of tools that |
| Addon developers are recommended to use to keep compatibility between past and |
| future releases of V8 and Node.js. See the `nan` [examples][] for an |
| illustration of how it can be used. |
| |
| ## Addon examples |
| |
| Following are some example Addons intended to help developers get started. The |
| examples make use of the V8 APIs. Refer to the online [V8 reference][] for help |
| with the various V8 calls, and V8's [Embedder's Guide][] for an explanation of |
| several concepts used such as handles, scopes, function templates, etc. |
| |
| Each of these examples using the following `binding.gyp` file: |
| |
| { |
| "targets": [ |
| { |
| "target_name": "addon", |
| "sources": [ "addon.cc" ] |
| } |
| ] |
| } |
| |
| In cases where there is more than one `.cc` file, simply add the additional |
| filename to the `sources` array. For example: |
| |
| "sources": ["addon.cc", "myexample.cc"] |
| |
| Once the `binding.gyp` file is ready, the example Addons can be configured and |
| built using `node-gyp`: |
| |
| $ node-gyp configure build |
| |
| |
| ### Function arguments |
| |
| Addons will typically expose objects and functions that can be accessed from |
| JavaScript running within Node.js. When functions are invoked from JavaScript, |
| the input arguments and return value must be mapped to and from the C/C++ |
| code. |
| |
| The following example illustrates how to read function arguments passed from |
| JavaScript and how to return a result: |
| |
| // addon.cc |
| #include <node.h> |
| |
| namespace demo { |
| |
| using v8::Exception; |
| using v8::FunctionCallbackInfo; |
| using v8::Isolate; |
| using v8::Local; |
| using v8::Number; |
| using v8::Object; |
| using v8::String; |
| using v8::Value; |
| |
| // This is the implementation of the "add" method |
| // Input arguments are passed using the |
| // const FunctionCallbackInfo<Value>& args struct |
| void Add(const FunctionCallbackInfo<Value>& args) { |
| Isolate* isolate = args.GetIsolate(); |
| |
| // Check the number of arguments passed. |
| if (args.Length() < 2) { |
| // Throw an Error that is passed back to JavaScript |
| isolate->ThrowException(Exception::TypeError( |
| String::NewFromUtf8(isolate, "Wrong number of arguments"))); |
| return; |
| } |
| |
| // Check the argument types |
| if (!args[0]->IsNumber() || !args[1]->IsNumber()) { |
| isolate->ThrowException(Exception::TypeError( |
| String::NewFromUtf8(isolate, "Wrong arguments"))); |
| return; |
| } |
| |
| // Perform the operation |
| double value = args[0]->NumberValue() + args[1]->NumberValue(); |
| Local<Number> num = Number::New(isolate, value); |
| |
| // Set the return value (using the passed in |
| // FunctionCallbackInfo<Value>&) |
| args.GetReturnValue().Set(num); |
| } |
| |
| void Init(Local<Object> exports) { |
| NODE_SET_METHOD(exports, "add", Add); |
| } |
| |
| NODE_MODULE(addon, Init) |
| |
| } // namespace demo |
| |
| Once compiled, the example Addon can be required and used from within Node.js: |
| |
| // test.js |
| const addon = require('./build/Release/addon'); |
| |
| console.log( 'This should be eight:', addon.add(3,5) ); |
| |
| |
| ### Callbacks |
| |
| It is common practice within Addons to pass JavaScript functions to a C++ |
| function and execute them from there. The following example illustrates how |
| to invoke such callbacks: |
| |
| // addon.cc |
| #include <node.h> |
| |
| namespace demo { |
| |
| using v8::Function; |
| using v8::FunctionCallbackInfo; |
| using v8::Isolate; |
| using v8::Local; |
| using v8::Null; |
| using v8::Object; |
| using v8::String; |
| using v8::Value; |
| |
| void RunCallback(const FunctionCallbackInfo<Value>& args) { |
| Isolate* isolate = args.GetIsolate(); |
| Local<Function> cb = Local<Function>::Cast(args[0]); |
| const unsigned argc = 1; |
| Local<Value> argv[argc] = { String::NewFromUtf8(isolate, "hello world") }; |
| cb->Call(Null(isolate), argc, argv); |
| } |
| |
| void Init(Local<Object> exports, Local<Object> module) { |
| NODE_SET_METHOD(module, "exports", RunCallback); |
| } |
| |
| NODE_MODULE(addon, Init) |
| |
| } // namespace demo |
| |
| Note that this example uses a two-argument form of `Init()` that receives |
| the full `module` object as the second argument. This allows the Addon |
| to completely overwrite `exports` with a single function instead of |
| adding the function as a property of `exports`. |
| |
| To test it, run the following JavaScript: |
| |
| // test.js |
| const addon = require('./build/Release/addon'); |
| |
| addon(function(msg){ |
| console.log(msg); // 'hello world' |
| }); |
| |
| Note that, in this example, the callback function is invoked synchronously. |
| |
| ### Object factory |
| |
| Addons can create and return new objects from within a C++ function as |
| illustrated in the following example. An object is created and returned with a |
| property `msg` that echoes the string passed to `createObject()`: |
| |
| // addon.cc |
| #include <node.h> |
| |
| namespace demo { |
| |
| using v8::FunctionCallbackInfo; |
| using v8::Isolate; |
| using v8::Local; |
| using v8::Object; |
| using v8::String; |
| using v8::Value; |
| |
| void CreateObject(const FunctionCallbackInfo<Value>& args) { |
| Isolate* isolate = args.GetIsolate(); |
| |
| Local<Object> obj = Object::New(isolate); |
| obj->Set(String::NewFromUtf8(isolate, "msg"), args[0]->ToString()); |
| |
| args.GetReturnValue().Set(obj); |
| } |
| |
| void Init(Local<Object> exports, Local<Object> module) { |
| NODE_SET_METHOD(module, "exports", CreateObject); |
| } |
| |
| NODE_MODULE(addon, Init) |
| |
| } // namespace demo |
| |
| To test it in JavaScript: |
| |
| // test.js |
| const addon = require('./build/Release/addon'); |
| |
| var obj1 = addon('hello'); |
| var obj2 = addon('world'); |
| console.log(obj1.msg+' '+obj2.msg); // 'hello world' |
| |
| |
| ### Function factory |
| |
| Another common scenario is creating JavaScript functions that wrap C++ |
| functions and returning those back to JavaScript: |
| |
| // addon.cc |
| #include <node.h> |
| |
| namespace demo { |
| |
| using v8::Function; |
| using v8::FunctionCallbackInfo; |
| using v8::FunctionTemplate; |
| using v8::Isolate; |
| using v8::Local; |
| using v8::Object; |
| using v8::String; |
| using v8::Value; |
| |
| void MyFunction(const FunctionCallbackInfo<Value>& args) { |
| Isolate* isolate = args.GetIsolate(); |
| args.GetReturnValue().Set(String::NewFromUtf8(isolate, "hello world")); |
| } |
| |
| void CreateFunction(const FunctionCallbackInfo<Value>& args) { |
| Isolate* isolate = args.GetIsolate(); |
| |
| Local<FunctionTemplate> tpl = FunctionTemplate::New(isolate, MyFunction); |
| Local<Function> fn = tpl->GetFunction(); |
| |
| // omit this to make it anonymous |
| fn->SetName(String::NewFromUtf8(isolate, "theFunction")); |
| |
| args.GetReturnValue().Set(fn); |
| } |
| |
| void Init(Local<Object> exports, Local<Object> module) { |
| NODE_SET_METHOD(module, "exports", CreateFunction); |
| } |
| |
| NODE_MODULE(addon, Init) |
| |
| } // namespace demo |
| |
| To test: |
| |
| // test.js |
| const addon = require('./build/Release/addon'); |
| |
| var fn = addon(); |
| console.log(fn()); // 'hello world' |
| |
| |
| ### Wrapping C++ objects |
| |
| It is also possible to wrap C++ objects/classes in a way that allows new |
| instances to be created using the JavaScript `new` operator: |
| |
| // addon.cc |
| #include <node.h> |
| #include "myobject.h" |
| |
| namespace demo { |
| |
| using v8::Local; |
| using v8::Object; |
| |
| void InitAll(Local<Object> exports) { |
| MyObject::Init(exports); |
| } |
| |
| NODE_MODULE(addon, InitAll) |
| |
| } // namespace demo |
| |
| Then, in `myobject.h`, the wrapper class inherits from `node::ObjectWrap`: |
| |
| // myobject.h |
| #ifndef MYOBJECT_H |
| #define MYOBJECT_H |
| |
| #include <node.h> |
| #include <node_object_wrap.h> |
| |
| namespace demo { |
| |
| class MyObject : public node::ObjectWrap { |
| public: |
| static void Init(v8::Local<v8::Object> exports); |
| |
| private: |
| explicit MyObject(double value = 0); |
| ~MyObject(); |
| |
| static void New(const v8::FunctionCallbackInfo<v8::Value>& args); |
| static void PlusOne(const v8::FunctionCallbackInfo<v8::Value>& args); |
| static v8::Persistent<v8::Function> constructor; |
| double value_; |
| }; |
| |
| } // namespace demo |
| |
| #endif |
| |
| In `myobject.cc`, implement the various methods that are to be exposed. |
| Below, the method `plusOne()` is exposed by adding it to the constructor's |
| prototype: |
| |
| // myobject.cc |
| #include "myobject.h" |
| |
| namespace demo { |
| |
| using v8::Function; |
| using v8::FunctionCallbackInfo; |
| using v8::FunctionTemplate; |
| using v8::Isolate; |
| using v8::Local; |
| using v8::Number; |
| using v8::Object; |
| using v8::Persistent; |
| using v8::String; |
| using v8::Value; |
| |
| Persistent<Function> MyObject::constructor; |
| |
| MyObject::MyObject(double value) : value_(value) { |
| } |
| |
| MyObject::~MyObject() { |
| } |
| |
| void MyObject::Init(Local<Object> exports) { |
| Isolate* isolate = exports->GetIsolate(); |
| |
| // Prepare constructor template |
| Local<FunctionTemplate> tpl = FunctionTemplate::New(isolate, New); |
| tpl->SetClassName(String::NewFromUtf8(isolate, "MyObject")); |
| tpl->InstanceTemplate()->SetInternalFieldCount(1); |
| |
| // Prototype |
| NODE_SET_PROTOTYPE_METHOD(tpl, "plusOne", PlusOne); |
| |
| constructor.Reset(isolate, tpl->GetFunction()); |
| exports->Set(String::NewFromUtf8(isolate, "MyObject"), |
| tpl->GetFunction()); |
| } |
| |
| void MyObject::New(const FunctionCallbackInfo<Value>& args) { |
| Isolate* isolate = args.GetIsolate(); |
| |
| if (args.IsConstructCall()) { |
| // Invoked as constructor: `new MyObject(...)` |
| double value = args[0]->IsUndefined() ? 0 : args[0]->NumberValue(); |
| MyObject* obj = new MyObject(value); |
| obj->Wrap(args.This()); |
| args.GetReturnValue().Set(args.This()); |
| } else { |
| // Invoked as plain function `MyObject(...)`, turn into construct call. |
| const int argc = 1; |
| Local<Value> argv[argc] = { args[0] }; |
| Local<Function> cons = Local<Function>::New(isolate, constructor); |
| args.GetReturnValue().Set(cons->NewInstance(argc, argv)); |
| } |
| } |
| |
| void MyObject::PlusOne(const FunctionCallbackInfo<Value>& args) { |
| Isolate* isolate = args.GetIsolate(); |
| |
| MyObject* obj = ObjectWrap::Unwrap<MyObject>(args.Holder()); |
| obj->value_ += 1; |
| |
| args.GetReturnValue().Set(Number::New(isolate, obj->value_)); |
| } |
| |
| } // namespace demo |
| |
| To build this example, the `myobject.cc` file must be added to the |
| `binding.gyp`: |
| |
| { |
| "targets": [ |
| { |
| "target_name": "addon", |
| "sources": [ |
| "addon.cc", |
| "myobject.cc" |
| ] |
| } |
| ] |
| } |
| |
| Test it with: |
| |
| // test.js |
| const addon = require('./build/Release/addon'); |
| |
| var obj = new addon.MyObject(10); |
| console.log( obj.plusOne() ); // 11 |
| console.log( obj.plusOne() ); // 12 |
| console.log( obj.plusOne() ); // 13 |
| |
| ### Factory of wrapped objects |
| |
| Alternatively, it is possible to use a factory pattern to avoid explicitly |
| creating object instances using the JavaScript `new` operator: |
| |
| var obj = addon.createObject(); |
| // instead of: |
| // var obj = new addon.Object(); |
| |
| First, the `createObject()` method is implemented in `addon.cc`: |
| |
| // addon.cc |
| #include <node.h> |
| #include "myobject.h" |
| |
| namespace demo { |
| |
| using v8::FunctionCallbackInfo; |
| using v8::Isolate; |
| using v8::Local; |
| using v8::Object; |
| using v8::String; |
| using v8::Value; |
| |
| void CreateObject(const FunctionCallbackInfo<Value>& args) { |
| MyObject::NewInstance(args); |
| } |
| |
| void InitAll(Local<Object> exports, Local<Object> module) { |
| MyObject::Init(exports->GetIsolate()); |
| |
| NODE_SET_METHOD(module, "exports", CreateObject); |
| } |
| |
| NODE_MODULE(addon, InitAll) |
| |
| } // namespace demo |
| |
| In `myobject.h`, the static method `NewInstance()` is added to handle |
| instantiating the object. This method takes the place of using `new` in |
| JavaScript: |
| |
| // myobject.h |
| #ifndef MYOBJECT_H |
| #define MYOBJECT_H |
| |
| #include <node.h> |
| #include <node_object_wrap.h> |
| |
| namespace demo { |
| |
| class MyObject : public node::ObjectWrap { |
| public: |
| static void Init(v8::Isolate* isolate); |
| static void NewInstance(const v8::FunctionCallbackInfo<v8::Value>& args); |
| |
| private: |
| explicit MyObject(double value = 0); |
| ~MyObject(); |
| |
| static void New(const v8::FunctionCallbackInfo<v8::Value>& args); |
| static void PlusOne(const v8::FunctionCallbackInfo<v8::Value>& args); |
| static v8::Persistent<v8::Function> constructor; |
| double value_; |
| }; |
| |
| } // namespace demo |
| |
| #endif |
| |
| The implementation in `myobject.cc` is similar to the previous example: |
| |
| // myobject.cc |
| #include <node.h> |
| #include "myobject.h" |
| |
| namespace demo { |
| |
| using v8::Function; |
| using v8::FunctionCallbackInfo; |
| using v8::FunctionTemplate; |
| using v8::Isolate; |
| using v8::Local; |
| using v8::Number; |
| using v8::Object; |
| using v8::Persistent; |
| using v8::String; |
| using v8::Value; |
| |
| Persistent<Function> MyObject::constructor; |
| |
| MyObject::MyObject(double value) : value_(value) { |
| } |
| |
| MyObject::~MyObject() { |
| } |
| |
| void MyObject::Init(Isolate* isolate) { |
| // Prepare constructor template |
| Local<FunctionTemplate> tpl = FunctionTemplate::New(isolate, New); |
| tpl->SetClassName(String::NewFromUtf8(isolate, "MyObject")); |
| tpl->InstanceTemplate()->SetInternalFieldCount(1); |
| |
| // Prototype |
| NODE_SET_PROTOTYPE_METHOD(tpl, "plusOne", PlusOne); |
| |
| constructor.Reset(isolate, tpl->GetFunction()); |
| } |
| |
| void MyObject::New(const FunctionCallbackInfo<Value>& args) { |
| Isolate* isolate = args.GetIsolate(); |
| |
| if (args.IsConstructCall()) { |
| // Invoked as constructor: `new MyObject(...)` |
| double value = args[0]->IsUndefined() ? 0 : args[0]->NumberValue(); |
| MyObject* obj = new MyObject(value); |
| obj->Wrap(args.This()); |
| args.GetReturnValue().Set(args.This()); |
| } else { |
| // Invoked as plain function `MyObject(...)`, turn into construct call. |
| const int argc = 1; |
| Local<Value> argv[argc] = { args[0] }; |
| Local<Function> cons = Local<Function>::New(isolate, constructor); |
| args.GetReturnValue().Set(cons->NewInstance(argc, argv)); |
| } |
| } |
| |
| void MyObject::NewInstance(const FunctionCallbackInfo<Value>& args) { |
| Isolate* isolate = args.GetIsolate(); |
| |
| const unsigned argc = 1; |
| Local<Value> argv[argc] = { args[0] }; |
| Local<Function> cons = Local<Function>::New(isolate, constructor); |
| Local<Object> instance = cons->NewInstance(argc, argv); |
| |
| args.GetReturnValue().Set(instance); |
| } |
| |
| void MyObject::PlusOne(const FunctionCallbackInfo<Value>& args) { |
| Isolate* isolate = args.GetIsolate(); |
| |
| MyObject* obj = ObjectWrap::Unwrap<MyObject>(args.Holder()); |
| obj->value_ += 1; |
| |
| args.GetReturnValue().Set(Number::New(isolate, obj->value_)); |
| } |
| |
| } // namespace demo |
| |
| Once again, to build this example, the `myobject.cc` file must be added to the |
| `binding.gyp`: |
| |
| { |
| "targets": [ |
| { |
| "target_name": "addon", |
| "sources": [ |
| "addon.cc", |
| "myobject.cc" |
| ] |
| } |
| ] |
| } |
| |
| Test it with: |
| |
| // test.js |
| const createObject = require('./build/Release/addon'); |
| |
| var obj = createObject(10); |
| console.log( obj.plusOne() ); // 11 |
| console.log( obj.plusOne() ); // 12 |
| console.log( obj.plusOne() ); // 13 |
| |
| var obj2 = createObject(20); |
| console.log( obj2.plusOne() ); // 21 |
| console.log( obj2.plusOne() ); // 22 |
| console.log( obj2.plusOne() ); // 23 |
| |
| |
| ### Passing wrapped objects around |
| |
| In addition to wrapping and returning C++ objects, it is possible to pass |
| wrapped objects around by unwrapping them with the Node.js helper function |
| `node::ObjectWrap::Unwrap`. The following examples shows a function `add()` |
| that can take two `MyObject` objects as input arguments: |
| |
| // addon.cc |
| #include <node.h> |
| #include <node_object_wrap.h> |
| #include "myobject.h" |
| |
| namespace demo { |
| |
| using v8::FunctionCallbackInfo; |
| using v8::Isolate; |
| using v8::Local; |
| using v8::Number; |
| using v8::Object; |
| using v8::String; |
| using v8::Value; |
| |
| void CreateObject(const FunctionCallbackInfo<Value>& args) { |
| MyObject::NewInstance(args); |
| } |
| |
| void Add(const FunctionCallbackInfo<Value>& args) { |
| Isolate* isolate = args.GetIsolate(); |
| |
| MyObject* obj1 = node::ObjectWrap::Unwrap<MyObject>( |
| args[0]->ToObject()); |
| MyObject* obj2 = node::ObjectWrap::Unwrap<MyObject>( |
| args[1]->ToObject()); |
| |
| double sum = obj1->value() + obj2->value(); |
| args.GetReturnValue().Set(Number::New(isolate, sum)); |
| } |
| |
| void InitAll(Local<Object> exports) { |
| MyObject::Init(exports->GetIsolate()); |
| |
| NODE_SET_METHOD(exports, "createObject", CreateObject); |
| NODE_SET_METHOD(exports, "add", Add); |
| } |
| |
| NODE_MODULE(addon, InitAll) |
| |
| } // namespace demo |
| |
| In `myobject.h`, a new public method is added to allow access to private values |
| after unwrapping the object. |
| |
| // myobject.h |
| #ifndef MYOBJECT_H |
| #define MYOBJECT_H |
| |
| #include <node.h> |
| #include <node_object_wrap.h> |
| |
| namespace demo { |
| |
| class MyObject : public node::ObjectWrap { |
| public: |
| static void Init(v8::Isolate* isolate); |
| static void NewInstance(const v8::FunctionCallbackInfo<v8::Value>& args); |
| inline double value() const { return value_; } |
| |
| private: |
| explicit MyObject(double value = 0); |
| ~MyObject(); |
| |
| static void New(const v8::FunctionCallbackInfo<v8::Value>& args); |
| static v8::Persistent<v8::Function> constructor; |
| double value_; |
| }; |
| |
| } // namespace demo |
| |
| #endif |
| |
| The implementation of `myobject.cc` is similar to before: |
| |
| // myobject.cc |
| #include <node.h> |
| #include "myobject.h" |
| |
| namespace demo { |
| |
| using v8::Function; |
| using v8::FunctionCallbackInfo; |
| using v8::FunctionTemplate; |
| using v8::Isolate; |
| using v8::Local; |
| using v8::Object; |
| using v8::Persistent; |
| using v8::String; |
| using v8::Value; |
| |
| Persistent<Function> MyObject::constructor; |
| |
| MyObject::MyObject(double value) : value_(value) { |
| } |
| |
| MyObject::~MyObject() { |
| } |
| |
| void MyObject::Init(Isolate* isolate) { |
| // Prepare constructor template |
| Local<FunctionTemplate> tpl = FunctionTemplate::New(isolate, New); |
| tpl->SetClassName(String::NewFromUtf8(isolate, "MyObject")); |
| tpl->InstanceTemplate()->SetInternalFieldCount(1); |
| |
| constructor.Reset(isolate, tpl->GetFunction()); |
| } |
| |
| void MyObject::New(const FunctionCallbackInfo<Value>& args) { |
| Isolate* isolate = args.GetIsolate(); |
| |
| if (args.IsConstructCall()) { |
| // Invoked as constructor: `new MyObject(...)` |
| double value = args[0]->IsUndefined() ? 0 : args[0]->NumberValue(); |
| MyObject* obj = new MyObject(value); |
| obj->Wrap(args.This()); |
| args.GetReturnValue().Set(args.This()); |
| } else { |
| // Invoked as plain function `MyObject(...)`, turn into construct call. |
| const int argc = 1; |
| Local<Value> argv[argc] = { args[0] }; |
| Local<Function> cons = Local<Function>::New(isolate, constructor); |
| args.GetReturnValue().Set(cons->NewInstance(argc, argv)); |
| } |
| } |
| |
| void MyObject::NewInstance(const FunctionCallbackInfo<Value>& args) { |
| Isolate* isolate = args.GetIsolate(); |
| |
| const unsigned argc = 1; |
| Local<Value> argv[argc] = { args[0] }; |
| Local<Function> cons = Local<Function>::New(isolate, constructor); |
| Local<Object> instance = cons->NewInstance(argc, argv); |
| |
| args.GetReturnValue().Set(instance); |
| } |
| |
| } // namespace demo |
| |
| Test it with: |
| |
| // test.js |
| const addon = require('./build/Release/addon'); |
| |
| var obj1 = addon.createObject(10); |
| var obj2 = addon.createObject(20); |
| var result = addon.add(obj1, obj2); |
| |
| console.log(result); // 30 |
| |
| ### AtExit hooks |
| |
| An "AtExit" hook is a function that is invoked after the Node.js event loop |
| has ended by before the JavaScript VM is terminated and Node.js shuts down. |
| "AtExit" hooks are registered using the `node::AtExit` API. |
| |
| #### void AtExit(callback, args) |
| |
| * `callback`: `void (*)(void*)` - A pointer to the function to call at exit. |
| * `args`: `void*` - A pointer to pass to the callback at exit. |
| |
| Registers exit hooks that run after the event loop has ended but before the VM |
| is killed. |
| |
| AtExit takes two parameters: a pointer to a callback function to run at exit, |
| and a pointer to untyped context data to be passed to that callback. |
| |
| Callbacks are run in last-in first-out order. |
| |
| The following `addon.cc` implements AtExit: |
| |
| // addon.cc |
| #undef NDEBUG |
| #include <assert.h> |
| #include <stdlib.h> |
| #include <node.h> |
| |
| namespace demo { |
| |
| using node::AtExit; |
| using v8::HandleScope; |
| using v8::Isolate; |
| using v8::Local; |
| using v8::Object; |
| |
| static char cookie[] = "yum yum"; |
| static int at_exit_cb1_called = 0; |
| static int at_exit_cb2_called = 0; |
| |
| static void at_exit_cb1(void* arg) { |
| Isolate* isolate = static_cast<Isolate*>(arg); |
| HandleScope scope(isolate); |
| Local<Object> obj = Object::New(isolate); |
| assert(!obj.IsEmpty()); // assert VM is still alive |
| assert(obj->IsObject()); |
| at_exit_cb1_called++; |
| } |
| |
| static void at_exit_cb2(void* arg) { |
| assert(arg == static_cast<void*>(cookie)); |
| at_exit_cb2_called++; |
| } |
| |
| static void sanity_check(void*) { |
| assert(at_exit_cb1_called == 1); |
| assert(at_exit_cb2_called == 2); |
| } |
| |
| void init(Local<Object> exports) { |
| AtExit(sanity_check); |
| AtExit(at_exit_cb2, cookie); |
| AtExit(at_exit_cb2, cookie); |
| AtExit(at_exit_cb1, exports->GetIsolate()); |
| } |
| |
| NODE_MODULE(addon, init); |
| |
| } // namespace demo |
| |
| Test in JavaScript by running: |
| |
| // test.js |
| const addon = require('./build/Release/addon'); |
| |
| [online]: https://v8docs.nodesource.com/ |
| [libuv]: https://github.com/libuv/libuv |
| [download]: https://github.com/nodejs/node-addon-examples |
| [node-gyp]: https://github.com/nodejs/node-gyp |
| [V8 reference]: https://v8docs.nodesource.com/ |
| [Embedder's Guide]: https://developers.google.com/v8/embed |
| [Native Abstrations for Node.js]: https://github.com/nodejs/nan |
| [examples]: https://github.com/nodejs/nan/tree/master/examples/ |
| [bindings]: https://github.com/TooTallNate/node-bindings |
| [Linking to Node.js' own dependencies]: #linking-to-nodejs-own-dependencies |
| [installation instructions]: https://github.com/nodejs/node-gyp#installation |