How to check for correct type when calling ObjectWrap::Unwrap in a Nodejs add-on?

一世执手 提交于 2019-12-05 05:23:53

EDIT: a better method than the bare V8 one below is to use NanHasInstance (https://github.com/rvagg/nan#api_nan_has_instance)

In MyObject::Init:

Local<FunctionTemplate> tpl = NanNew<FunctionTemplate>(New);
tpl->SetClassName(NanNew<String>("MyObject"));
...
NanAssignPersistent(prototype, tpl);

where prototype is a static Persistent<FunctionTemplate> member of MyObject.

Use like this:

if (NanHasInstance(prototype, handle)) {
    MyObject* obj = ObjectWrap::Unwrap<MyObject>(handle);
    ...
}

With the caveat that this is my first go at writing a Node addon, I solved this exact problem by checking the prototype of the object with my own wrapper around UnWrap.

Here's a patch to the addon factory class demo showing the method: https://github.com/petli/node-addon-examples/commit/d3e92cd060a26da2623690718e78f9005db060a8

It will only support factory-generated objects, and not ones where a constructor is exposed so that users could inherit from the base class. However, that could be generalised by walking the prototype chain.

In summary, it grabs hold of the reference to the expected class prototype in MyObject::Init:

Local<Object> obj = constructor->NewInstance();
prototype = Persistent<Value>::New(obj->GetPrototype());

And then checks that before dereferencing the object:

MyObject* MyObject::CheckedUnWrap(Handle<Object> handle)
{
  if (!handle.IsEmpty() && handle->InternalFieldCount() == 1) {
    Handle<Value> objproto = handle->GetPrototype();
    if (objproto == prototype) {
      // OK, this is us
      return ObjectWrap::Unwrap<MyObject>(handle);
    }
  }

  ThrowException(Exception::TypeError(String::New("<this> is not a MyObject")));
  return NULL;
}

All functions then use CheckedUnWrap instead:

Handle<Value> MyObject::PlusOne(const Arguments& args) {
  HandleScope scope;

  MyObject* obj = CheckedUnWrap(args.This());
  if (obj) {
    obj->counter_ += 1;
    return scope.Close(Number::New(obj->counter_));
  }
  else {
    // Invalid type, an exception has been thrown so return an empty value
    return Handle<Value>();
  }
}

I was also considering adding an internal field and set that to some some magic pointer, but then the code would have depended on that node::ObjectWrap wouldn't change how it uses the internal fields.

Update: Since NanHasInstance has been deprecated, the new solution to this answer is to use bool FunctionTemplate::HasInstance(Local<Value> object). This function returns true if the given object is an instance of this function template.

Persistent<Function> Wrapper::constructor;
Persistent<FunctionTemplate> Wrapper::tpl;

Then in your Wrapper::Init() function, set the public Persistent object:

Local<FunctionTemplate> tpl = FunctionTemplate::New(isolate, New);
// ...
Wrapper::tpl.Reset(isolate, tpl);

Now to unwrap:

Local<FunctionTemplate> wrapper_tpl = Wrapper::tpl.Get(isolate);
if (!(wrapper_tpl->HasInstance(args[0]))) {
  isolate->ThrowException(Exception::TypeError(
      String::NewFromUtf8(isolate, "Argument must be a Wrapper object")));
  return;
}
// Now we are safe to call ObjectWrap::Unwrap
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!