Skip to content

Node.js native module programming

bellbind edited this page Dec 4, 2015 · 11 revisions

Separate application algorithm codes and Node.js plug codes

  • Application codes should be removed any node.js and v8 related definitions.
    • it can use another languages for application code. e.g. C, Objective-C
  • Node.js plug codes are almost for convert arguments and result of the native functions in pure C/C++.
  • Explicitly use the namespace node and v8 and Nan without using namespace node or using v8::XXX as a glue code

About "nan"

Early days, v8 API was drastically changed incompatibly when version updated.

  • type signature and returning result of native functions for v8::FunctionTemplate
  • v8::Isolate introduction and hiding from object construction
  • converting between native and v8 values
  • ...

The nan was born for the layer of hiding the detail of v8 API.

But nan API is also changed when nan-2 that based on "maybe" returns. It is also reduced abstraction layers for old v8 used node-0.10 age.

The current node>=4 may apply only nan-2.

About v8 Handles

3 Handle (smart pointer) type exists as smart pointer of RAII (release the holding resource by its destructor)

  • v8::Local: automatically managed by v8::HandleScope
  • v8::Persistent: explicitly call Reset(obj) to hold or Reset() to release
  • v8::Global: for global scope, use Reset(..) same as Persistent

v8::HandleScope instance is stacked memory manager for Local handle.

  • v8::HandleScope scope: make scope upon top of HandleScope stack.
  • v8::Local<...> v = ...: managed by stack top scope. the scope destructor release all managed handle

(The current v8 implicitly use HandleScope just before call native function from JS code via v8::FunctionTemplate, so explicit using HandleScope is for using v8 functions from outside of v8 call stacks: e.g. from multi threading, or from native event callbacks)

Maybe and MaybeLocal function results

In v8 and nan, many function returns "maybe" value instead of throwing exceptions. You can check the result is exist or not via the "maybe" values, and can acquire the real value when existed.

In v8, two maybe types exist:

  • Maybe<native_type>: is for a native value or "nothing"
  • MaybeLocal<V8Type>: is for a v8 objects via v8::Local handle or "empty"

Maybe methods:

  • existence: m.IsJust() or m.IsNothing()
  • acquire: m.FromJust() or m.FromMaybe(defaultVal)

MaybeLocal methods:

  • existence: m.IsEmpty()
  • acquire: m.ToLocalChecked() or m.FromMaybe(defaultObj)

Convert C type to v8::Value

use nan

  • char* => String: Nan::New(s).ToLocalCheched()
  • String => char*: char* s = *Nan::Utf8Value(str);
  • int32_t => Integer: Nan::New<int32_t>(i)
  • Integer => int32_t: Nan::To<int32_t>(v).FromJust()
  • uint32_t => Integer: Nan::New<uint32_t>(i)
  • Integer => uint32_t: Nan::To<uint32_t>(v).FromJust()

Keep Handle of C object in v8 Object

New is the native method for constructor function

    auto clazz = Nan::New(v8::FunctionTemplate>(New);
    clazz->InstanceTemplate()->SetInternalFieldCount(1);

n of SetInternalFieldCount(n) is count of use SetInernalField(i, v) (that is called inside
objectWrap->Wrap(self), so n is 1 when holding only ObjectWrap itself)

Watching fd by uv_poll pattern

  // prepare data struct for holding objects used in callback process
  struct CallbackData {
    v8::Persistent<v8::Object> thisObj;
    std::unique_ptr<Nan::Callback> callback;
  };
  
  void callbackByUv(uv_poll_t* handle, int status, int events) {
    HandleScope scope; // explicitly required because on outside of JS call chain
    auto data = static_cast<CallbackData*>(handle->data); // get user data
    uv_poll_stop(handle); // stop poll
    // handle should delete in close callback (after closed)
    // DON'T delete in uv callback!!
    uv_close(reinterpret_cast<uv_handle_t*>(handle), 
             [](uv_handle_t* handle) -> void {delete handle;}); 

    callbackCalling(data); //e.g. do some action for fd then call the callback by the action result)
    
    // destroy callback data
    data->thisObj.Reset(); // release thisObj
    delete data; // callback will delete inside
  }
  
  NAN_METHOD(method) {
    // prepare data used in the uv callback (after polled)
    auto data = new CallbackData;
    auto thisObj = info.Holder();
    data->thisObj.Reset(thisObj);
    data->callback.reset(new Nan::Callback(info[0].As<v8::Function>()));
    
    int fd = acquireFd(thisObj); // get fd from Object

    // poll fd
    uv_poll_t* handle = new uv_poll_t;
    handle->data = data;
    uv_poll_init(uv_default_loop(), handle, fd);
    uv_poll_start(handle, UV_READABLE, callbackByUv); // set callback
  }

Examples

See the annotated native module examples for node-4 and nan-2: