I have 2 C++ classes exposed as javascript classes, VanillaOption
and NoomraEngine
, both inheriting from ObjectWrap
.
In the following method in NoomraEngine
, I m supposed to receive a previously "wrapped" VanillaOption
:
Handle<Value>
NoomraEngine::Price(const Arguments& args) {
HandleScope scope;
Local<Object> object = args[0]->ToObject(); // VanillaOption expected in args[0]
VanillaOption* equityOption = ObjectWrap::Unwrap<VanillaOption>(object);
Local<Number> x = Number::New(this->price(equityOption));
return scope.Close(x);
}
Everything works fine except that if I pass the wrong type to the method, node crashes in ObjectWrap::Unwrap
.
My question is how can I make sure that I've received the correct type in args[0]
?
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
node::Buffer::HasInstance(args[0]);
Also see: http://www.lamedoc.com/node.js/classnode_1_1Buffer.html#a4cc6fa734527df461641c6e93e6d1197
来源:https://stackoverflow.com/questions/8994196/how-to-check-for-correct-type-when-calling-objectwrapunwrap-in-a-nodejs-add-on