javascriptc++node.jsnode.js-addonnode.js-nan

Proper Method for Storing JavaScript Instances in Nan::ObjectWrap C++ Class


Let's say I have two classes that are developed in C++, but are exposed using Nan NodeJS native modules. For example, a "Sprite" and "Texture" class.

In the JavaScript, I want to be able to have a Sprite store a Texture as if it were just another property:

// Local instance of texture
var t = new Texture();

// Global instances of Sprites that won't be GC'd
global.spr = new Sprite();
global.spr2 = new Sprite();

// Same texture can be assigned to multiple sprites
spr.texture = t;
spr2.texture = t;

console.log(spr.texture);

On the C++ side of things, the "texture" property of the Sprite would be a Getter/Setter that stores or retrieves the Texture. The question is though, what would be the proper way to store an instance of Texture internally (from the provided v8::Local)? Like, what do I need to do to register it as "owned" by the Sprite and cause it not to get GC'd as it probably would be in the example above?


Solution

  • You can have a Persistent handle as a member of your Sprite class that stores the Texture instance. The Sprite destructor needs to reset the the handle to avoid a leak.

    class Sprite : public Nan::ObjectWrap {
    public:
      Nan::Persistent<v8::Object> textureHandle;
    
      static NAN_SETTER(SetTexture) {
        Sprite* sprite = Nan::ObjectWrap::Unwrap<Sprite>(info.This());
        if (Nan::New(Texture::constructor)->HasInstance(value)) {
          sprite->textureHandle.Reset(value);
        } else {
          // value wasn't a Texture instance; do something
        }
      }
    
      static NAN_GETTER(GetTexture) {
        Sprite* sprite = Nan::ObjectWrap::Unwrap<Sprite>(info.This());
        Isolate* iso = Isolate::GetCurrent();
        if (sprite->textureHandle.IsEmpty()) {
          // Texture hasn't been set yet; do something
        } else {
          info.GetReturnValue().Set(sprite->TextureHandle.Get(iso));
        }
      }
    
      ~Sprite() {
        textureHandle.Reset();
      }
    }
    

    (Not shown: the initialization code to register the getter/setter.)