Native Abstractions for Node.js (NAN)

repository·main·Indexed 25 days ago

https://github.com/nodejs/nan

A C++ header library providing macros and utilities to simplify the development of native Node.js addons. NAN ensures compatibility across Node.js versions (0.8 to 26) by abstracting V8 and Node core changes. It provides cross-version compatible implementations for HandleScopes, Persistent references, JavaScript value creation via Nan::New(), type conversion with Nan::To(), and asynchronous execution through AsyncWorker, AsyncProgressWorker, and AsyncQueueWorker.

Tokens
18.1K
Snippets
46
Records
91
Agent score
86%

What's inside nan

  1. Convert NAN 1 source code to NAN 2 using 1to2

    main

    The 1to2 tool naively converts source code files from NAN 1 to NAN 2.

    Warning: This tool performs in-place rewrites of input files. It may produce erroneous conversions, false positives, or missed opportunities. Always ensure you have backups of your source code before running this tool, and manually review all changes to perform necessary touchups.

  2. Explore NAN usage examples

    main

    For practical implementations of NAN, you can refer to several resources:

    • Node Add-on Examples: A repository of various Node.js addons.
    • Async Pi Estimation: A specific example within the NAN repository demonstrating Monte Carlo Pi estimation using async work.
    • nan-example-eol: An example showing newline detection implemented as a native addon.
    • C++ Test Suite: The project's own test suite contains numerous code snippets useful for implementation.
  3. Safely unwrap objects using Holder() vs This()

    main

    When using Nan::ObjectWrap::Unwrap<T>(handle), ensure the handle is a valid JavaScript object wrapped by your class.

    • In Prototype Methods: Use info.Holder() if the method was installed via Nan::SetPrototypeMethod(). In Node.js 0.12+, info.This() is also safe as the invocation will be rejected if the type doesn't match.
    • In Accessors: If the accessor is defined on the prototype, do not use info.Holder(). Instead, define accessors on the instance template or use info.This() after verifying it is a valid object.

    Warning: In Node.js 0.10 and earlier, calling Unwrap on info.This() when the method is on the prototype chain (but not the instance itself) can cause crashes or memory corruption.

  4. Configure binding.gyp to include NAN

    main
    To use #include <nan.h> in your C++ files, you must add the NAN include path to your binding.gyp file. You can dynamically resolve the path using a node command within the include_dirs section.
  5. Avoid deprecated Nan::MakeCallback()

    main

    The Nan::MakeCallback() functions are deprecated in Node.js 10+ because they do not provide a mechanism to preserve async context.

    Recommendation: Use the AsyncResource class and AsyncResource::runInAsyncScope instead of Nan::MakeCallback or v8::Function#Call() to ensure compatibility with async_hooks, domains, and async debugging.

    NAN_DEPRECATED
    v8::Local<v8::Value> Nan::MakeCallback(v8::Local<v8::Object> target,
                                           v8::Local<v8::Function> func,
                                           int argc,
                                           v8::Local<v8::Value>* argv);
    
    NAN_DEPRECATED
    v8::Local<v8::Value> Nan::MakeCallback(v8::Local<v8::Object> target,
                                           v8::Local<v8::String> symbol,
                                           int argc,
                                           v8::Local<v8::Value>* argv);
    
    NAN_DEPRECATED
    v8::Local<v8::Value> Nan::MakeCallback(v8::Local<v8::Object> target,
                                           const char* method,
                                           int argc,
                                           v8::Local<v8::Value>* argv);
  6. Troubleshoot compiling Node.js 0.12 on OSX

    main

    If you are attempting to compile against Node.js 0.12 on OSX using modern compilers, you may encounter a V8 header error where CreateHandle is a protected member of v8::HandleScope.

    Error Example:

    CXX(target) Release/obj.target/accessors/cpp/accessors.o
    In file included from ../cpp/accessors.cpp:9:
    In file included from ../../nan.h:51:
    In file included from /Users/ofrobots/.node-gyp/0.12.18/include/node/node.h:61:
    /Users/ofrobots/.node-gyp/0.12.18/include/node/v8.h:5800:54: error: 'CreateHandle' is a protected member of 'v8::HandleScope'
      return Handle<T>(reinterpret_cast<T*>(HandleScope::CreateHandle(
                                            ~~~~~~~~~~~~~^~~~~~~~~~~~~~

    Workarounds:

    1. Use an older compiler compatible with the Node.js 0.12 era.
    2. Apply a source patch to your local V8 headers (corresponding to Node 0.12) to make v8::Handle a friend of v8::HandleScope.
  7. Create a factory for wrapped objects

    main

    You can implement a factory pattern where a JavaScript function returns new instances of a wrapped C++ class. This is done by creating a Nan::ObjectWrap class and providing a static method (e.g., NewInstance) that uses Nan::NewInstance to return a new object.

    C++ Implementation Pattern

    1. Inherit from Nan::ObjectWrap.
    2. Set InstanceTemplate()->SetInternalFieldCount(1).
    3. Implement a New method to handle IsConstructCall() (for new MyObject()) and non-construct calls (for factory usage).
    4. Use obj->Wrap(info.This()) inside the constructor logic.
    class MyFactoryObject : public Nan::ObjectWrap {
     public:
      static NAN_MODULE_INIT(Init) {
        v8::Local<v8::FunctionTemplate> tpl = Nan::New<v8::FunctionTemplate>(New);
        tpl->InstanceTemplate()->SetInternalFieldCount(1);
    
        Nan::SetPrototypeMethod(tpl, "getValue", GetValue);
    
        constructor().Reset(Nan::GetFunction(tpl).ToLocalChecked());
      }
    
      static NAN_METHOD(NewInstance) {
        v8::Local<v8::Function> cons = Nan::New(constructor());
        double value = info[0]->IsNumber() ? Nan::To<double>(info[0]).FromJust() : 0;
        const int argc = 1;
        v8::Local<v8::Value> argv[1] = {Nan::New(value)};
        info.GetReturnValue().Set(Nan::NewInstance(cons, argc, argv).ToLocalChecked());
      }
    
      inline double value() const { return value_; }
    
     private:
      explicit MyFactoryObject(double value = 0) : value_(value) {}
      ~MyFactoryObject() {}
    
      static NAN_METHOD(New) {
        if (info.IsConstructCall()) {
          double value = info[0]->IsNumber() ? Nan::To<double>(info[0]).FromJust() : 0;
          MyFactoryObject * obj = new MyFactoryObject(value);
          obj->Wrap(info.This());
          info.GetReturnValue().Set(info.This());
        } else {
          const int argc = 1;
          v8::Local<v8::Value> argv[argc] = {info[0]};
          v8::Local<v8::Function> cons = Nan::New(constructor());
          info.GetReturnValue().Set(Nan::NewInstance(cons, argc, argv).ToLocalChecked());
        }
      }
    
      static NAN_METHOD(GetValue) {
        MyFactoryObject* obj = ObjectWrap::Unwrap<MyFactoryObject>(info.Holder());
        info.GetReturnValue().Set(obj->value_);
      }
    
      static inline Nan::Persistent<v8::Function> & constructor() {
        static Nan::Persistent<v8::Function> my_constructor;
        return my_constructor;
      }
    
      double value_;
    };
    
    NAN_MODULE_INIT(Init) {
      MyFactoryObject::Init(target);
      Nan::Set(target,
        Nan::New<v8::String>("newFactoryObjectInstance").ToLocalChecked(),
        Nan::GetFunction(
          Nan::New<v8::FunctionTemplate>(MyFactoryObject::NewInstance)).ToLocalChecked()
      );
    }
    
    NODE_MODULE(wrappedobjectfactory, Init)
  8. Declare JavaScript-accessible methods

    main

    A method must follow the Nan::FunctionCallback signature. A HandleScope is implicitly created for you, so you do not need to declare a new one within the method.

    Signature:

    typedef void(*FunctionCallback)(const FunctionCallbackInfo<v8::Value>&);

    You can use the NAN_METHOD(methodname) macro for compatibility with NAN v1 declarations.

    void MethodName(const Nan::FunctionCallbackInfo<v8::Value>& info) {
      ...
    }