0

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?

5
  • Can you confirm these details: (1) the C++ code is the native implementation of myAddon.greet(), and (2) you are attempting to access the object myObj1 from this C++ code? If that is the case, then is myObj1.name a function? Commented Apr 10, 2017 at 18:10
  • @cdhowie - Yes indeed, the C++ code is the native implementation of myAddon.greet(), and I pass in myObj1.name, intending this to be the address of the name function in the instance. I also tried passing in the entire object , and using the C++ code to extract the method from the object, but same result. It's driving me nuts. Commented Apr 10, 2017 at 18:27
  • In the example you have given, you cannot obtain myObj1 in your C++ code. The correct way to do this would be to bind this from the JavaScript side: myAddon.greet(myObj1.name.bind(myObj1));, just as you must do for every other kind of callback. Then you simply don't care about it on the C++ side. Commented Apr 10, 2017 at 18:33
  • @cdhowie, many thanks for helping, I am still learning all of this, and I haven't come across the bind() stuff. I will go read up on it. If you want to change your comment to an answer I will accept it. Commented Apr 10, 2017 at 18:36
  • @cdhowie Works like a charm. I guess I have some JavaScript learning to do. Many thanks. Commented Apr 10, 2017 at 18:41

1 Answer 1

1

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))
Sign up to request clarification or add additional context in comments.

9 Comments

Thanks for taking the time to educate - a great answer.
at the risk of being really cheeky - my real life JavaScript object has 5 methods that I will want to call from C++. Do I need to pass 5 bound functions, or is there away to pass a bound object?
@Greycon I mean, you can just pass myObj1 directly. You would just have to document the structure this object is expected to have so that other developers passing in objects can make sure they are passing you something you can use.
I originally did pass myObj1 directly , then used the C++ v8 api to extract the 5 methods from the object and called them as needed , but that's when I first encountered the "this" problem .
Essentially the JavaScript object needs to supply 5 Lifecycle methods which I want a C++ addon to manage and call. Im thinking of making a single bound function now, which I will pass to the C++ addon, and this function can act as a facade for all 5 functions when called by the addon
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.