Из JS я вызываю функцию C++ следующим образом:
var req = new IO.HttpRequest(IO.RequestType.get);
req.data({ i: 'jTKvNf9w' }).send('http://pastebin.com/raw.php', function (content) { console.log(content); });
Запрос обрабатывается асинхронно, и после его завершения обращается вызов. В C++ функция отправки выглядит так:
void XmlHttpRequest::open(const Utils::String& str, ::JS::FunctionObjPtr callback) {
mCallback = callback;
mRequest->open(str);
}
И позже, если запрос будет завершен:
void XmlHttpRequest::onComplete(Utils::String content) {
sUIMgr->getDispatcher()->pushFrame(Gl::Dispatcher::Priority::Low, [this, content]() {
::JS::FunctionObjPtr f = mCallback;
f->callVoid(content);
});
}
Рамка push помещает функцию в очередь для выполнения в основном потоке, где настроены все сценарии.
Теперь проблема в callVoid
Я получаю нарушение прав доступа, когда HeapObject :: GetHeap() вызывает MemoryChunk::FromAddress(reinterpret_cast<Address>(this))->heap();
, Это функции v8. Проблема в том, что this
-Pointer в HeapObject равен 0xCCCCCCCC, что означает его неинициализированное значение. this
-Pointer происходит от ручки, хранящейся в JS :: FunctionObjPtr (ЬурейаЯ для станд :: shared_ptr).
Сначала я думал, что с моим FunctionObj
что-то не так. Я получаю это:
template<typename T>
static TYPE_RET(FunctionObjPtr) ObjectWrap::unwrap(v8::Handle<v8::Value>& value) {
if (value->IsFunction() == false) {
TYPE_ERR("Value is not a function");
}
return std::make_shared<FunctionObj>(value);
}
TYPE_RET просто выполняет некоторые метапрограммирование шаблонов. Функция FunctionObj
выглядит так:
class FunctionObj
{
v8::Handle<v8::Value> mHandle;
public:
FunctionObj(v8::Handle<v8::Value>& fun) {
mHandle = fun;
}
FunctionObj() { }
operator bool () {
return mHandle.IsEmpty() == false;
}
template<typename... Args>
void callVoid(const Args&... args) {
std::vector<v8::Handle<v8::Value>> arguments;
addArgument(arguments, args...);
v8::TryCatch tc;
v8::Handle<v8::Function>::Cast(mHandle)->Call(mHandle, arguments.size(), arguments.data());
if (tc.HasCaught()) {
throw JS::Exception(tc.Exception(), tc.StackTrace());
}
}
};
Когда я вызываю функцию в XmlHttpRequest::open
где ее "зарегистрировано", она работает. Поэтому сначала я думал, что объект получает gc'ed, но для того, чтобы убедиться, что он никогда не будет собран в FunctionObj :: FunctionObj, я создал v8 :: Persistent из дескриптора. Тем не менее он падает. Я даже сделал v8 :: Persistent слабым, чтобы посмотреть, действительно ли он собирается, но слабый callback никогда не вызван.
Другие вещи, которые я проверил прямо перед вызовом:
Дополнительная информация: это не ограничивается только v8 :: Handle <v8 :: Function>. Это также происходит, если я попытаюсь сохранить объект и позже получить доступ к его свойствам. Я просто наклоняюсь к чему-либо внутри этой лямбды, которая использует ручку.
Как я уже сказал в том, что я пробовал, я посмотрел на v8::Persistent
. Оказывается, я использовал это неправильно.
Что я сделал:
class FunctionObj
{
v8::Handle<v8::Value> mHandle;
v8::Persistent<v8::Value> mPersistent;
public:
FunctionObj(v8::Handle<v8::Value>& fun) {
mPersistent.Reset(v8::Isolate::GetCurrent(), fun);
mHandle = fun;
}
FunctionObj() { }
operator bool () {
return mHandle.IsEmpty() == false;
}
template<typename... Args>
void callVoid(const Args&... args) {
std::vector<v8::Handle<v8::Value>> arguments;
addArgument(arguments, args...);
v8::TryCatch tc;
v8::Handle<v8::Function>::Cast(mHandle)->Call(mHandle, arguments.size(), arguments.data());
if (tc.HasCaught()) {
throw JS::Exception(tc.Exception(), tc.StackTrace());
}
}
};
Что я должен был сделать
class FunctionObj
{
v8::Persistent<v8::Value> mPersistent;
public:
FunctionObj(v8::Handle<v8::Value>& fun) {
mPersistent.Reset(v8::Isolate::GetCurrent(), fun);
}
FunctionObj() { if(mPersistent) { mPersistent.Dispose(); } }
operator bool () {
return mHandle.IsEmpty() == false;
}
template<typename... Args>
void callVoid(const Args&... args) {
std::vector<v8::Handle<v8::Value>> arguments;
addArgument(arguments, args...);
v8::Local<v8::Function> fun = v8::Local<v8::Function>::New(v8::Isolate::GetCurrent(), mPersistent.As<v8::Function>());
v8::TryCatch tc;
fun->Call(fun, arguments.size(), arguments.data());
if (tc.HasCaught()) {
throw JS::Exception(tc.Exception(), tc.StackTrace());
}
}
};
Постоянное я создал не означает, что хранимый мной дескриптор не уничтожается, а только то, что позже будет возможно запросить новый дескриптор объекта и что объект не будет удален.
Теперь все работает как шарм!