2012-01-24 2 views
8

Ho 2 classi C++ esposte come classi javascript, VanillaOption e NoomraEngine, entrambe ereditate da ObjectWrap.Come verificare il tipo corretto quando si chiama ObjectWrap :: Unwrap in un componente aggiuntivo Nodejs?

Nel seguente metodo nella NoomraEngine, I m suppone di ricevere una precedenza "avvolto" 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); 
} 

Tutto funziona bene, tranne che se mi passate il tipo sbagliato il metodo, gli arresti dei nodi in ObjectWrap::Unwrap.

La mia domanda è: come posso essere sicuro di aver ricevuto il tipo corretto in args[0]?

risposta

3

EDIT: un metodo migliore di nudo V8 quella qui sotto è usare 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); 

dove prototype è un statico Persistent<FunctionTemplate> membro di MyObject.

Usa come questo:

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

Con l'avvertenza che questo è il mio primo tentativo di scrivere un addon Nodo, ho risolto questo problema esatto controllando il prototipo dell'oggetto con il mio wrapper UnWrap.

Ecco una patch per la classe addon fabbrica demo che mostra il metodo: https://github.com/petli/node-addon-examples/commit/d3e92cd060a26da2623690718e78f9005db060a8

Sosterrà solo gli oggetti in fabbrica generato, e non quelle in cui un costruttore è esposto in modo che gli utenti potevano ereditare dalla classe base. Tuttavia, ciò potrebbe essere generalizzato camminando sulla catena del prototipo.

In sintesi, si afferra il riferimento al prototipo di classe previsto MyObject::Init:

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

E poi controlla che prima dereferencing l'oggetto:

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; 
} 

Tutte le funzioni quindi utilizzare CheckedUnWrap invece :

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 als o considerando di aggiungere un campo interno e impostarlo su qualche puntatore magico, ma il codice sarebbe dipeso dal fatto che lo node::ObjectWrap non cambierebbe il modo in cui utilizza i campi interni.

+0

Grazie per la risposta aggiornata. Sto rivisitando questo problema e risulta che la tua risposta è "migliore" della risposta precedentemente accettata. Quindi accettando il tuo – BigONotation

3

Aggiornamento: Dal NanHasInstance è stato deprecato, la nuova soluzione a questa risposta è quello di utilizzare bool FunctionTemplate::HasInstance(Local<Value> object). Questa funzione restituisce true se l'oggetto dato è un'istanza di questo modello di funzione.

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

Poi nella funzione Wrapper::Init(), impostare l'oggetto persistente pubblico:

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

Ora a scartare:

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