Reputation: 9938
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 :'(
Upvotes: 4
Views: 3901
Reputation: 136
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.
Upvotes: 3
Reputation: 13655
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>
Upvotes: 0