Using Spidermonkey v27:
What is the proper way to "retain" and then asynchronously call a temporary JS function from C++?
JS code:
myFunction(function(){
console.log("The function works");
});
C++ code:
bool js_myFunction(JSContext* cx, uint32_t argc, jsval* vp)
{
if (argc == 1)
{
implement_async_function(cx, vp);
JS_SET_RVAL(cx, vp, JSVAL_NULL);
return true;
}
return false;
}
static JSContext* savedContext;
static jsval* savedVal;
void implement_async_function(JSContext* cx, jsval* vp)
{
// if the function is called immediately, everything is okay!
jsval retVal;
JS_CallFunctionValue(cx, nullptr, *vp, 0, nullptr, &retVal);
// "The function works"
// if some work has to be done before calling the callback...
savedContext = cx;
savedVal = vp;
start_async_process();
}
void async_process_complete()
{
jsval retVal;
JS_CallFunctionValue(savedContext, nullptr, *savedVal, 0, nullptr, &retVal);
// "<no filename="filename">:0:true is not a function"
// or else it crashes...
}
void alternate_implement_async_function(JSContext* cx, jsval* vp)
{
// also tried this:
savedContext = cx;
savedVal = vp;
JS_AddValueRoot(savedContext, savedVal);
start_async_process();
// and this:
savedContext = cx;
savedVal = new jsval(*vp);
JS_AddValueRoot(savedContext, savedVal);
start_async_process();
// and using JS::RootedValue
// and using JS::Heap<JS::Value>
// and using the global context instead of the saved context
}
I've read through SpiderMonkey's documents:
https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey/JSAPI_Reference/JS::Value
https://developer.mozilla.org/en-US/docs/SpiderMonkey/GC_Rooting_Guide
https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey/JSAPI_reference/JS_AddRoot
https://developer.mozilla.org/en-US/docs/SpiderMonkey/JSAPI_Reference/JS_CallFunctionValue
And checked this StackOverflow post Spidermonkey and Garbage Collection
And tried even making the JS callback function a global function which should never be garbage collected. (This is not what I want though.)
You didn't mention the behavior of your program when trying to call the function asynchronously, but I assume that it would seg fault when trying to call the function since the GC might have moved it. You need a way to store the function object (it's not a value as in your code). JS::Rooted
is not acceptable in this situation since stack rooting expects gc things to root in a LIFO ordering. Thus, you need to store it in a JS::Heap<JSObject*>
handle. The JS::Heap
handle expects to live on the heap instead of the stack (maybe in a list or tree structure). The caveat with JS::Heap
handles is that they need to be traced by the GC so the underlying objects can be relocated and your handle updated. Tracing is discussed in this question.
Assuming your object is now stored properly and traced, you should be able to call it at any time without any issue.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With