1

I'm new to nodejs addons and am learning on the fly working with existing code. I have a situation where I don't understand how a specific C++ constructor is being called nor how the value of an argument is being set.

There are two pure C++ constructors:

Nan::Persistent<v8::Function> Event::constructor;

// Construct a blank event from the context metadata
Event::Event() {
oboe_event_init(&event, oboe_context_get());
}

// Construct a new event and point an edge at another
Event::Event(const oboe_metadata_t *md, bool addEdge) {
// both methods copy metadata (version, task_id, flags) from
// md to this->event and create a new random op_id.
if (addEdge) {
    // create_event automatically adds edge in event to md
    // (so event points an edge to the op_id in md).
    oboe_metadata_create_event(md, &event);
} else {
    // initializes new Event with md's task_id & new random op_id;
    // no edges set
    oboe_event_init(&event, md);
}
}

// Remember to cleanup the struct when garbage collected
Event::~Event() {
oboe_event_destroy(&event);
}

There are three NewInstance declarations: one with two arguments, one with one, and one with none.

v8::Local<v8::Object> Event::NewInstance(Metadata* md, bool addEdge) {
Nan::EscapableHandleScope scope;

const unsigned argc = 2;
v8::Local<v8::Value> argv[argc] = {
    Nan::New<v8::External>(md),
    Nan::New(addEdge)
};
v8::Local<v8::Function> cons = Nan::New<v8::Function>(constructor);
v8::Local<v8::Object> instance = cons->NewInstance(argc, argv);

return scope.Escape(instance);
}

v8::Local<v8::Object> Event::NewInstance(Metadata* md) {
Nan::EscapableHandleScope scope;

const unsigned argc = 1;
v8::Local<v8::Value> argv[argc] = { Nan::New<v8::External>(md) };
v8::Local<v8::Function> cons = Nan::New<v8::Function>(constructor);
v8::Local<v8::Object> instance = cons->NewInstance(argc, argv);

return scope.Escape(instance);
}

v8::Local<v8::Object> Event::NewInstance() {
Nan::EscapableHandleScope scope;

const unsigned argc = 0;
v8::Local<v8::Value> argv[argc] = {};
v8::Local<v8::Function> cons = Nan::New<v8::Function>(constructor);
v8::Local<v8::Object> instance = cons->NewInstance(argc, argv);

return scope.Escape(instance);
}

And there is the module init code that performs a bit of magic to make a constructor function template:

// Wrap the C++ object so V8 can understand it
void Event::Init(v8::Local<v8::Object> exports) {
Nan::HandleScope scope;

// Prepare constructor template
v8::Local<v8::FunctionTemplate> ctor = Nan::New<v8::FunctionTemplate>(New);
ctor->InstanceTemplate()->SetInternalFieldCount(1);
ctor->SetClassName(Nan::New("Event").ToLocalChecked());

// Statics
Nan::SetMethod(ctor, "startTrace", Event::startTrace);

// Prototype
Nan::SetPrototypeMethod(ctor, "addInfo", Event::addInfo);
Nan::SetPrototypeMethod(ctor, "addEdge", Event::addEdge);
Nan::SetPrototypeMethod(ctor, "getMetadata", Event::getMetadata);
Nan::SetPrototypeMethod(ctor, "toString", Event::toString);

constructor.Reset(ctor->GetFunction());
Nan::Set(exports, Nan::New("Event").ToLocalChecked(), ctor->GetFunction());
}

Finally, there is the code that invokes Event::NewInstance:

 Metadata* md = new Metadata(oboe_context_get());
 info.GetReturnValue().Set(Event::NewInstance(md));

Via logging I see that the NewInstance(md) call ends up calling the two argument C++ constructor Event::Event(const oboe_metadata_t *md, bool addEdge) and that the boolean, addEdge is true.

How does the single-argument NewInstance call end up invoking the two argument constructor and how does addEdge get set to true?

I'm not clear on how the three variations of v8::Local<v8::Object> Event::NewInstance() declarations get mapped to the C++ constructor but it seems like it must be in the line v8::Local<v8::FunctionTemplate> ctor = Nan::New<v8::FunctionTemplate>(New);

Any help would be appreciated.

Here is the missing code showing the "New" function that I had skipped over because the comment said it creates a new JavaScript instance. But it's clearly the missing piece.

// Creates a new Javascript instance
NAN_METHOD(Event::New) {
if (!info.IsConstructCall()) {
    return Nan::ThrowError("Event() must be called as a constructor");
}

Event* event;
if (info.Length() > 0 && info[0]->IsExternal()) {
    Metadata* md = static_cast<Metadata*>(info[0].As<v8::External>()->Value());
    oboe_metadata_t* context = &md->metadata;

    bool addEdge = true;
    if (info.Length() == 2 && info[1]->IsBoolean()) {
    addEdge = info[1]->BooleanValue();
    }

    event = new Event(context, addEdge);
} else {
    event = new Event();
}

event->Wrap(info.This());
info.GetReturnValue().Set(info.This());
}
bmacnaughton
  • 4,950
  • 3
  • 27
  • 36
  • 1
    your `New` Function is calling the actual `Event::Event`. Do you have that code, too? – pergy Nov 17 '17 at 12:47
  • Am adding it. I was missing that piece and most of my question seems kind of dumb right now because that clearly shows how it gets invoked. Can you help me with what the "NewInstance()" calls are needed for? Are they only used for invocation in C++ code? Am updating the question to include the code that I should have found before. Too much new I guess. – bmacnaughton Nov 17 '17 at 12:52
  • Sorry my update nixed your formatting improvements. – bmacnaughton Nov 17 '17 at 12:59
  • 1
    yes, `New` shows what the problem is. About this whole biolerplate codes: I'm not clear neither. I use the same nan patterns, but without knowing exact rationals behind the scenes :) Np for formatting – pergy Nov 17 '17 at 12:59
  • Thanks for your help. If you want to post an answer I'll accept it. If not, please know that you got me past a mental block on this. It's hard to find people to discuss this stuff with. – bmacnaughton Nov 17 '17 at 13:03
  • Welcome :) I won't post answer, feel free to delete the question! ;) – pergy Nov 17 '17 at 13:04
  • 1
    I'll leave the question. It's kind of a dumb question but it might help someone understand something more. Everything I read on this adds another piece of the puzzle. In this particular case I had read that the function template was being invoked with (what I took to be) a generic "New", not a specific one that had been declared. Exceedingly blind in retrospect but I hope my blindness can help someone else. – bmacnaughton Nov 17 '17 at 13:08

0 Answers0