| # Addons |
| |
| Addons are dynamically-linked shared objects. They can provide glue to C and |
| C++ libraries. The API (at the moment) is rather complex, involving |
| knowledge of several libraries: |
| |
| - V8 JavaScript, a C++ library. Used for interfacing with JavaScript: |
| creating objects, calling functions, etc. 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][], C event loop library. Anytime one needs to wait for a file |
| descriptor to become readable, wait for a timer, or wait for a signal |
| to be received, one will need to interface with libuv. That is, if you |
| perform any I/O, libuv will need to be used. |
| |
| - Internal Node.js libraries. The most important class is `node::ObjectWrap` |
| which you will likely want to derive from. |
| |
| - Others. Look in `deps/` for what else is available. |
| |
| Node.js statically compiles all its dependencies into the executable. |
| When compiling your module, you don't need to worry about linking to |
| any of these libraries. |
| |
| All of the following examples are available for [download][] and may |
| be used as a starting-point for your own Addon. |
| |
| ## Hello world |
| |
| To get started, let's make a small Addon which is the C++ equivalent of |
| the following JavaScript code: |
| |
| module.exports.hello = function() { return 'world'; }; |
| |
| First we create a 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: |
| |
| 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` needs to match the filename of the final binary (excluding |
| the .node suffix). |
| |
| The source code needs to be built into `addon.node`, the binary Addon. To |
| do this, we create a file called `binding.gyp` which describes the configuration |
| to build your module in a JSON-like format. This file gets compiled by |
| [node-gyp][]. |
| |
| { |
| "targets": [ |
| { |
| "target_name": "addon", |
| "sources": [ "hello.cc" ] |
| } |
| ] |
| } |
| |
| The next step is to generate the appropriate project build files for the |
| current platform. Use `node-gyp configure` for that. |
| |
| Now you will have either a `Makefile` (on Unix platforms) or a `vcxproj` file |
| (on Windows) in the `build/` directory. Next, invoke the `node-gyp build` |
| command. |
| |
| Now you have your compiled `.node` bindings file! The compiled bindings end up |
| in `build/Release/`. |
| |
| You can now use the binary addon in a Node.js project `hello.js` by pointing |
| `require` to the recently built `hello.node` module: |
| |
| // hello.js |
| const addon = require('./build/Release/addon'); |
| |
| console.log(addon.hello()); // 'world' |
| |
| Please see patterns below for further information or |
| <https://github.com/arturadib/node-qt> for an example in production. |
| |
| |
| ## Addon patterns |
| |
| Below are some addon patterns to help you get started. Consult 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. |
| |
| In order to use these examples, you need to compile them using `node-gyp`. |
| Create 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 file name to |
| the `sources` array. For example: |
| |
| "sources": ["addon.cc", "myexample.cc"] |
| |
| Now that you have your `binding.gyp` ready, you can configure and build the |
| addon: |
| |
| $ node-gyp configure build |
| |
| |
| ### Function arguments |
| |
| The following pattern illustrates how to read arguments from JavaScript |
| function calls and return a result. This is the main and only needed source |
| `addon.cc`: |
| |
| // 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; |
| |
| void Add(const FunctionCallbackInfo<Value>& args) { |
| Isolate* isolate = args.GetIsolate(); |
| |
| if (args.Length() < 2) { |
| isolate->ThrowException(Exception::TypeError( |
| String::NewFromUtf8(isolate, "Wrong number of arguments"))); |
| return; |
| } |
| |
| if (!args[0]->IsNumber() || !args[1]->IsNumber()) { |
| isolate->ThrowException(Exception::TypeError( |
| String::NewFromUtf8(isolate, "Wrong arguments"))); |
| return; |
| } |
| |
| double value = args[0]->NumberValue() + args[1]->NumberValue(); |
| Local<Number> num = Number::New(isolate, value); |
| |
| args.GetReturnValue().Set(num); |
| } |
| |
| void Init(Local<Object> exports) { |
| NODE_SET_METHOD(exports, "add", Add); |
| } |
| |
| NODE_MODULE(addon, Init) |
| |
| } // namespace demo |
| |
| You can test it with the following JavaScript snippet: |
| |
| // test.js |
| const addon = require('./build/Release/addon'); |
| |
| console.log( 'This should be eight:', addon.add(3,5) ); |
| |
| |
| ### Callbacks |
| |
| You can pass JavaScript functions to a C++ function and execute them from |
| there. Here's `addon.cc`: |
| |
| // 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 snippet: |
| |
| // test.js |
| const addon = require('./build/Release/addon'); |
| |
| addon(function(msg){ |
| console.log(msg); // 'hello world' |
| }); |
| |
| |
| ### Object factory |
| |
| You can create and return new objects from within a C++ function with this |
| `addon.cc` pattern, which returns an object with 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 |
| |
| This pattern illustrates how to create and return a JavaScript function that |
| wraps a C++ function: |
| |
| // 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 |
| |
| Here, we will create a wrapper for a C++ object/class `MyObject` that can be |
| instantiated in JavaScript through the `new` operator. First, prepare the main |
| module `addon.cc`: |
| |
| // 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`, make your wrapper inherit 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 |
| |
| And in `myobject.cc`, implement the various methods that you want to expose. |
| Here we expose the method `plusOne` 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 |
| |
| 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 |
| |
| This is useful when you want to be able to create native objects without |
| explicitly instantiating them with the `new` operator in JavaScript. For |
| example: |
| |
| var obj = addon.createObject(); |
| // instead of: |
| // var obj = new addon.Object(); |
| |
| Let's register our `createObject` method 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`, we now introduce the static method `NewInstance` that takes |
| care of instantiating the object. In other words, it does the job of `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 is similar to the above in `myobject.cc`: |
| |
| // 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 |
| |
| 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, you can pass them around |
| by unwrapping them with the Node.js helper function `node::ObjectWrap::Unwrap`. |
| In the following `addon.cc`, we introduce a function `add()` that can take on |
| two `MyObject` objects: |
| |
| // 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 |
| |
| To make things interesting, we introduce a public method in `myobject.h` so we |
| can probe 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 |
| #### 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. |
| |
| Callbacks are run in last-in first-out order. 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. |
| |
| The file `addon.cc` implements AtExit below: |
| |
| // 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]: http://izs.me/v8-docs/main.html |
| [Embedder's Guide]: https://code.google.com/apis/v8/embed.html |