繁体   English   中英

从V8中的C ++回调调用Javascript函数

[英]Calling Javascript function from a C++ callback in V8

我正在尝试在调用c ++回调时调用已注册的JS函数,但由于我认为是范围界定问题,因此我遇到了段错误。

 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()对Persistent的引用来调用回调函数,它将正确执行该函数。

我假设我需要一个储物柜或隔离柜? 看起来libuv的uv_queue_work()也许可以解决这个问题,但是由于我没有启动线程,所以我看不到如何使用它。

当您在代码中声明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));

(请注意, Persistent<T>上的operator*返回的是T*而不是更常规的T& ,请参见http://bespin.cz/~ondras/html/classv8_1_1Handle.html

您还必须调整callback以解决以下事实: context现在是指向Function的原始指针,如下所示:

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

在这里从原始Function指针创建Persistent<Function>是可以的,因为我们知道context实际上是一个持久对象。

为了简洁起见,我还将(*func)->Call(...)更改为func->Call(...) 对于V8手柄,它们执行相同的操作。

我知道这个问题有点老了,但是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();
  }

我相信此更新的目的是使暴露内存泄漏变得更加困难。 在节点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();

问题在于,在addEventListener中, Persistent<Function> fn在堆栈上分配,然后您将指向该指针的指针用作回调的上下文。

但是,因为fn是在堆栈上分配的,所以当addEventListener退出时,它会消失。 因此,有了回调context现在指向一些虚假值。

您应该分配一些堆空间,并将所需的所有数据放在那里的callback中。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM