从C ++在V8回调调用JavaScript函数(Calling Javascript functi

2019-07-04 23:46发布

我试图调用JS注册功能时,一个C ++调用回调函数,但我得到了什么,我认为是一个范围问题段错误。

 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。需要注意的是,如果我直接从使用addEventListener()的引用持续调用回调函数,它正确执行功能。

我假设我需要一个储物柜或隔离? 它也像libuv的uv_queue_work()也许能够解决这个问题,但因为我不启动线程,我看不出你如何使用它。

Answer 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*上的Persistent<T>返回一个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把手。



Answer 2:

我知道这个问题是有点老了,但一直在v0.10的NodeJS到v0.12一个非常重大的更新。 V8改变V8 ::持久性的行为。 V8 ::持久不再V8 ::句柄继承。 我更新了一些代码,发现下面的工作...

  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();
  }

我相信,本次更新的目的是使其更难揭露内存泄漏。 在节点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();


Answer 3:

问题是,在的addEventListener, Persistent<Function> fn被分配在堆栈上,然后你正在做的指针,作为回调上下文中使用。

但是,因为fn是在栈上分配的,当它消失addEventListener退出。 所以withing回调context现在都指向了一些伪造的值。

您应该分配一些堆空间,并把所有你需要的数据callback出现。



文章来源: Calling Javascript function from a C++ callback in V8