如何在 v8 中获取正确的 js 上下文
How to get the correct js context in v8
我正在尝试 nodejs 中的 c++ 插件,这是我的测试代码
let obj = new addon.MyClass(function (v) {
console.log(v);
});
obj.run(1);
setTimeout(() => {
obj.run(3); // [TypeError: obj.run is not a function]
}, 1000);
当我在js中延迟一秒再次调用该函数时,会出现这个错误
obj.run(3);
^
[TypeError: obj.run is not a function]
这是插件的C++代码。插件导出一个对象。在js中实例化对象时,需要初始化一个回调函数。当运行函数被调用时,回调被调用。我不知道问题出在哪里。我该如何修复代码
#include <node.h>
#include <node_object_wrap.h>
using namespace v8;
#define V8_STR(str) String::NewFromUtf8(isolate, str, NewStringType::kNormal).ToLocalChecked()
class MyClass : public node::ObjectWrap
{
private:
Eternal<Context>* context_;
Eternal<Function>* cb_;
public:
explicit MyClass(Eternal<Context>* context, Eternal<Function>* cb) : cb_(cb), context_(context) {
}
static void Init(Local<Object> exports) {
Isolate* isolate = exports->GetIsolate();
Local<Context> context = isolate->GetCurrentContext();
Local<ObjectTemplate> class_tpl = ObjectTemplate::New(isolate);
class_tpl->SetInternalFieldCount(1);
Local<Object> obj = class_tpl->NewInstance(context).ToLocalChecked();
Local<FunctionTemplate> tpl = FunctionTemplate::New(isolate, New, obj);
tpl->SetClassName(V8_STR("MyClass"));
tpl->InstanceTemplate()->SetInternalFieldCount(1);
NODE_SET_PROTOTYPE_METHOD(tpl, "run", run);
Local<Function> constructor = tpl->GetFunction(context).ToLocalChecked();
obj->SetInternalField(0, constructor);
exports->Set(context, V8_STR("MyClass"), constructor).FromJust();
};
static void New(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
Eternal<Context> e_context(isolate, isolate->GetCurrentContext());
Eternal<Function> e_cb(isolate, args[0].As<Function>());
MyClass* obj = new MyClass(&e_context, &e_cb);
obj->Wrap(args.This());
}
static void run(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
HandleScope scope(isolate);
auto context = Context::New(isolate);
MyClass* self = ObjectWrap::Unwrap<MyClass>(args.Holder());
const unsigned argc = 1;
Local<Value> argv[argc] = { args[0] };
// call callback
self->cb_->Get(isolate)->Call(context, Null(isolate), argc, argv);
}
};
void Initialize(Local<Object> exports) {
MyClass::Init(exports);
}
NODE_MODULE(NODE_GYP_MODULE_NAME, Initialize)
我的节点版本
λ node -v
v14.15.4
正如我在您提出的上一个问题中告诉您的那样:不要存储指向堆栈分配对象的指针。这与 V8 或上下文无关。
在函数New
中,Eternal<Function> e_cb
是一个堆栈分配的对象。构造函数调用 new MyClass(..., &e_cb)
创建并传递指向此堆栈分配对象的指针。但是一旦那个函数returns,它所有的堆栈分配对象都会被拆除,所以指针将指向一个无效的堆栈槽(可能被重用并因此填充“随机”数据)。当然,context_
也是如此,但你没有在任何地方使用它,所以它坏掉的事实是不可见的。
在这种特定情况下,您可以简单地按值传递 Eternal
,或者在 MyClass
构造函数中创建它。一般来说,我建议阅读 C++ 基础知识。
至于获取“正确”上下文:您已经在使用的 Isolate::GetCurrentContext()
有什么问题?
将 Eternal 初始化移至构造函数
class MyClass : public node::ObjectWrap
{
private:
Eternal<Function> cb_;
public:
explicit MyClass(const FunctionCallbackInfo<Value>& args)
{
Isolate* isolate = args.GetIsolate();
cb_ = Eternal<Function>(isolate, args[0].As<Function>());
}
...
static void New(const FunctionCallbackInfo<Value>& args) {
MyClass* obj = new MyClass(args);
obj->Wrap(args.This());
}
...
static void run(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
auto context = isolate->GetCurrentContext();
MyClass* self = ObjectWrap::Unwrap<MyClass>(args.Holder());
const unsigned argc = 1;
Local<Value> argv[argc] = { args[0] };
self->cb_.Get(isolate)->Call(context, Null(isolate), argc, argv);
}
}
我正在尝试 nodejs 中的 c++ 插件,这是我的测试代码
let obj = new addon.MyClass(function (v) {
console.log(v);
});
obj.run(1);
setTimeout(() => {
obj.run(3); // [TypeError: obj.run is not a function]
}, 1000);
当我在js中延迟一秒再次调用该函数时,会出现这个错误
obj.run(3);
^
[TypeError: obj.run is not a function]
这是插件的C++代码。插件导出一个对象。在js中实例化对象时,需要初始化一个回调函数。当运行函数被调用时,回调被调用。我不知道问题出在哪里。我该如何修复代码
#include <node.h>
#include <node_object_wrap.h>
using namespace v8;
#define V8_STR(str) String::NewFromUtf8(isolate, str, NewStringType::kNormal).ToLocalChecked()
class MyClass : public node::ObjectWrap
{
private:
Eternal<Context>* context_;
Eternal<Function>* cb_;
public:
explicit MyClass(Eternal<Context>* context, Eternal<Function>* cb) : cb_(cb), context_(context) {
}
static void Init(Local<Object> exports) {
Isolate* isolate = exports->GetIsolate();
Local<Context> context = isolate->GetCurrentContext();
Local<ObjectTemplate> class_tpl = ObjectTemplate::New(isolate);
class_tpl->SetInternalFieldCount(1);
Local<Object> obj = class_tpl->NewInstance(context).ToLocalChecked();
Local<FunctionTemplate> tpl = FunctionTemplate::New(isolate, New, obj);
tpl->SetClassName(V8_STR("MyClass"));
tpl->InstanceTemplate()->SetInternalFieldCount(1);
NODE_SET_PROTOTYPE_METHOD(tpl, "run", run);
Local<Function> constructor = tpl->GetFunction(context).ToLocalChecked();
obj->SetInternalField(0, constructor);
exports->Set(context, V8_STR("MyClass"), constructor).FromJust();
};
static void New(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
Eternal<Context> e_context(isolate, isolate->GetCurrentContext());
Eternal<Function> e_cb(isolate, args[0].As<Function>());
MyClass* obj = new MyClass(&e_context, &e_cb);
obj->Wrap(args.This());
}
static void run(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
HandleScope scope(isolate);
auto context = Context::New(isolate);
MyClass* self = ObjectWrap::Unwrap<MyClass>(args.Holder());
const unsigned argc = 1;
Local<Value> argv[argc] = { args[0] };
// call callback
self->cb_->Get(isolate)->Call(context, Null(isolate), argc, argv);
}
};
void Initialize(Local<Object> exports) {
MyClass::Init(exports);
}
NODE_MODULE(NODE_GYP_MODULE_NAME, Initialize)
我的节点版本
λ node -v
v14.15.4
正如我在您提出的上一个问题中告诉您的那样:不要存储指向堆栈分配对象的指针。这与 V8 或上下文无关。
在函数New
中,Eternal<Function> e_cb
是一个堆栈分配的对象。构造函数调用 new MyClass(..., &e_cb)
创建并传递指向此堆栈分配对象的指针。但是一旦那个函数returns,它所有的堆栈分配对象都会被拆除,所以指针将指向一个无效的堆栈槽(可能被重用并因此填充“随机”数据)。当然,context_
也是如此,但你没有在任何地方使用它,所以它坏掉的事实是不可见的。
在这种特定情况下,您可以简单地按值传递 Eternal
,或者在 MyClass
构造函数中创建它。一般来说,我建议阅读 C++ 基础知识。
至于获取“正确”上下文:您已经在使用的 Isolate::GetCurrentContext()
有什么问题?
将 Eternal 初始化移至构造函数
class MyClass : public node::ObjectWrap
{
private:
Eternal<Function> cb_;
public:
explicit MyClass(const FunctionCallbackInfo<Value>& args)
{
Isolate* isolate = args.GetIsolate();
cb_ = Eternal<Function>(isolate, args[0].As<Function>());
}
...
static void New(const FunctionCallbackInfo<Value>& args) {
MyClass* obj = new MyClass(args);
obj->Wrap(args.This());
}
...
static void run(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
auto context = isolate->GetCurrentContext();
MyClass* self = ObjectWrap::Unwrap<MyClass>(args.Holder());
const unsigned argc = 1;
Local<Value> argv[argc] = { args[0] };
self->cb_.Get(isolate)->Call(context, Null(isolate), argc, argv);
}
}