I create an instance of a JavaScript object, like;
myObj1 = new myObject("Object1");
I then call a node.js c++ addon, which does a callback to the Javascript object...
myAddon = require('myAddon');
myAddon.greet(myObj1.name);
The c++ callback code looks like:
Local<Function> callback = Local<Function>::Cast(args[0]);
Isolate* isolate = args.GetIsolate();
const int argc = 1;
const char *parm = "Hello";
v8::Local<v8::Value> argv[argc] = { v8::String::NewFromUtf8(isolate, parm) };
callback->Call(isolate->GetCurrentContext()->Global(), argc, argv);
But the problem is that the binding doesn't callback to the instance of the myObject, rather it appears to callback to the base JavaScript class. So there is no instance data.
From what I have been able to read over the past 2 days, the first parameter to the Call() method somehow becomes the "this" pointer in v8. So I would guess the problem might be that I am using a Global context
Does anyone have a clue how to correctly callback to a JavaScript object on the heap?
The this
value is only set when you invoke a method in the form a.b(...)
or a[c](...)
where a
is any expression and b
is an attribute name (or c
is an expression evaluating as an attribute name) and the attribute value is a function.
That's a lot to unpack, but basically what it means is that when you do a.b.c(d)
then the function stored at a.b.c
is invoked with the value of the expression a.b
being bound to this
in that function's context.
That is the only time this kind of magic happens.
So, when you invoke myAddon.greet(myObj1.name)
, the function greet
(fetched from the object myAddon
) is invoked with this
being set to myAddon
. There is no other magic happening. In particular, myObj1.name
is evaluated, found to be a function, and this function object is passed as an argument. The detail that the function was obtained from myObj1
is not retained, which means that your C++ code as no mechanism it can use to obtain a reference to this object!
Instead, the proper way to do this in JavaScript is for the code passing the callback to bind this
as it needs, using the bind()
method of functions, like so:
myAddon.greet(myObj1.name.bind(myObj1));
Your C++ code will receive a proxy function object (bind()
creates a new function). This proxy will invoke the myObj1.name
function with this
being set to myObj1
, so your C++ code simply doesn't have to care about it.
To illustrate, the expression myObj1.name.bind(myObj1)
is roughly equivalent to
(function (o, f) {
return function () { return f.apply(o, arguments); };
}(myObj1, myObj1.name))