Подтвердить что ты не робот

Вызов функции Javascript из обратного вызова С++ в V8

Я пытаюсь вызвать зарегистрированную функцию JS при вызове callback С++, но я получаю segfault для того, что я предполагаю, это проблема определения области.

 Handle<Value> addEventListener( const Arguments& args ) {
    HandleScope scope;
    if (!args[0]->IsFunction()) {
        return ThrowException(Exception::TypeError(String::New("Wrong arguments")));
    }

    Persistent<Function> fn = Persistent<Function>::New(Handle<Function>::Cast(args[0]));
    Local<Number> num = Number::New(registerListener(&callback, &fn));
    scope.Close(num);
}

Когда происходит событие, вызывается следующий метод. Я предполагаю, что это, вероятно, происходит в другом потоке, к которому V8 выполняет JS.

void callback(int event, void* context ) {
    HandleScope scope;
    Local<Value> args[] = { Local<Value>::New(Number::New(event)) };
    Persistent<Function> *func = static_cast<Persistent<Function> *>(context);
    (* func)->Call((* func), 1, args);

    scope.Close(Undefined());
}

Это вызывает ошибку сегментации: 11. Обратите внимание, что если я вызываю функцию обратного вызова напрямую со ссылкой на Persistent из addEventListener(), она правильно выполняет эту функцию.

Я предполагаю, что мне нужен Locker или Isolate? Также похоже, что libuv uv_queue_work() может решить это, но поскольку я не запускаю поток, я не вижу, как вы его используете.

4b9b3361

Ответ 1

Когда вы объявляете Persistent<Function> fn в своем коде, fn является переменной, назначенной стеком.

fn - это Persistent<Function>, который является классом дескриптора, и он будет содержать указатель на некоторое выделенное кучей значение типа Function, но сам fn находится в стеке.

Это означает, что когда вы вызываете registerListener(&callback, &fn), &fn берет адрес дескриптора (тип Persistent<Function>), а не адрес Function в куче. Когда ваша функция выйдет, дескриптор будет уничтожен, но сам Function останется в куче.

Итак, в качестве исправления я предлагаю передать адрес Function вместо адреса дескриптора, например:

Persistent<Function> fn = Persistent<Function>::New(Handle<Function>::Cast(args[0]));
Local<Number> num = Number::New(registerListener(&callback, *fn));

(обратите внимание, что operator* на a Persistent<T> возвращает a T*, а не более условный T&, cf http://bespin.cz/~ondras/html/classv8_1_1Handle.html)

Вам также придется настроить callback, чтобы учесть тот факт, что context теперь является необработанным указателем на Function, например:

Persistent<Function> func = static_cast<Function*>(context);
func->Call((* func), 1, args);

Создание Persistent<Function> из необработанного указателя функции здесь ОК, потому что мы знаем, что context на самом деле является постоянным объектом. Я также изменил (*func)->Call(...) на func->Call(...) для краткости; они делают то же самое для ручек V8.

Ответ 2

Я знаю, что этот вопрос немного устарел, но в nodejs v0.10 до v0.12 было довольно большое обновление. V8 изменил поведение v8:: Persistent. v8:: Persistent больше не наследуется от v8:: Handle. Я обновлял некоторый код и обнаружил, что следующее сработало...

  void resize(const v8::FunctionCallbackInfo<Value> &args) {
    Isolate *isolate = Isolate::GetCurrent();
    HandleScope scope(isolate);
    Persistent<Function> callback;
    callback.Reset(isolate, args[0].As<Function>())
    const unsigned argc = 2;
    Local<Value> argv[argc] = { Null(isolate), String::NewFromUtf8(isolate, "success") };
    Local<Function>::New(isolate, work->callback)->Call(isolate->GetCurrentContext()->Global(), argc, argv);
    callback.Reset();
  }

Я считаю, что цель этого обновления заключалась в том, чтобы сделать его более трудным для выявления утечек памяти. В node v0.10 вы сделали бы что-то вроде следующего...

  v8::Local<v8::Value> value = /* ... */;
  v8::Persistent<v8::Value> persistent = v8::Persistent<v8::Value>::New(value);
  // ...
  v8::Local<v8::Value> value_again = *persistent;
  // ...
  persistent.Dispose();
  persistent.Clear();

Ответ 3

Проблема заключается в том, что в addEventListener в стеке выделяется Persistent<Function> fn, а затем вы берете указатель на тот, который будет использоваться в качестве контекста для обратного вызова.

Но, поскольку fn выделяется в стеке, он исчезает, когда addEventListener завершается. Таким образом, при обратном вызове context теперь указывается некоторое фиктивное значение.

Вы должны выделить некоторое пространство кучи и поместить все данные, которые вам нужны, в callback.