简体   繁体   中英

How to call a javascript callback from C++

I'm trying to call a callback in V8 from another point of my function. So this piece of code register the callback:

        if (args.Length())
        {
            String::Utf8Value event(args[0]->ToString());
            if (event.length())
            {  
                Isolate* isolate = V8Interface::getCurrent()->getIsolate();

                Locker locker(isolate);
                HandleScope scope(isolate);

                callback cb = callback(isolate, Local<Function>::Cast(args[1]));

                 if(!events.count(*event))
                 {
                events[*event] = callbacks({ cb });
                 } 
                 else 
                {
                    events.find(*event)->second.push_back(cb);
                 }
            }
        }

And this one calls it:

 void trigger(std::string event)
    {

        Isolate* isolate = V8Interface::getCurrent()->getIsolate();

        Locker locker(isolate);
        HandleScope scope(isolate);

        if(events.count(event))
        {
            for(callback cb : events.find(event)->second)
            {
                Local<Function> local = Local<Function>::New(isolate, cb);
                local->Call(isolate->GetCurrentContext()->Global(), 0, {});
            }
        }
    }

The trigger function can be called anywhere at any time in my C++ code. I tried a simple example (init v8 then call trigger), and I get:

#
# Fatal error in C:\OgreSDK\Projects\whitedrop\third_party\io.js\deps\v8\src/api.h, line 386
# CHECK(allow_empty_handle || that != 0) failed
#

This is my main:

Scribe::V8Interface v8Interface = Scribe::V8Interface();
v8Interface.initialize();

for(Whitedrop::WorldEvent* event : Whitedrop::worldEvents)
{
    event->onStart();
}

You can get the whole source here:

https://github.com/whitedrop/whitedrop/tree/feature/v8

Line 386 is:

/**
   * Create a local handle for the content of another handle.
   * The referee is kept alive by the local handle even when
   * the original handle is destroyed/disposed.
   */
  V8_INLINE static Local<T> New(Isolate* isolate, Handle<T> that); // <<<<<<
  V8_INLINE static Local<T> New(Isolate* isolate,
                                const PersistentBase<T>& that);

EDIT: I tried, thanks to Ben Noordhuis, like this:

Isolate* isolate = V8Interface::getCurrent()->getIsolate();

Locker locker(isolate);
HandleScope scope(isolate);

callback cb;
cb.Reset(isolate, Local<Function>::Cast(args[1]));

And to call:

Isolate* isolate = V8Interface::getCurrent()->getIsolate();

Locker locker(isolate);
HandleScope scope(isolate);

if(events.count(event))
{
    for(callback cb : events.find(event)->second)
    {
        Local<Function> local = Local<Function>::New(isolate, cb);
        local->Call(isolate->GetCurrentContext()->Global(), 0, {});
    }
}

But same error :'(

Not sure wether author of question still need the answer. Anyway.

In continuing of previous answer about converting Local handlers to Persistent. There are some examples on Node.js C++ addons docs page:
https://nodejs.org/api/addons.html#addons_function_factory

For persistent objects they use static constructor method as Persistent<Function> . When module is inited constructor could be initialized by Reset method like this (actually i didn't find it's description in v8 docs - http://izs.me/v8-docs/classv8_1_1Persistent.html ):

// some where in external class or as global var
static Persistent<Function> persistentCallback;

// when need to init
persistentCallback.Reset(isolate, f_tmpl->GetFunction());

To invoke this method when it's needed you should use Local handle cast like this:

Local<Function> f = Local<Function>::New(isoloate,persistentCallback)

Be aware you can't use Handle pointers, because its ::New method doesn't fit for such arguments.

BTW. I figured out that I don't need to use Persistent handler in my code to store callback method in case I has already bound it inside JS context. It seems that it's properly managed by GC in that case. But there is no other way to share vars between c++ functions calls, for ex. when you store some protected data inside you module.

I think you should cast Local<T> to Persistent<T> before storing them into events . If you don't the GC from v8 may collect the functions at any time.

Handle<Function> args0 = Handle<Function>::Cast(args[0]);
Persistent<Function> pfn(args0);
callback cb = callback(isolate, pfn); // use Persistent<T> instead of Local<T>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM