Reputation: 2248
I am trying the c++ plugin in nodejs, this is my test code
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);
When I call the function again with a delay of one second in js, this error will appear
obj.run(3);
^
[TypeError: obj.run is not a function]
This is the C++ code of the plug-in. The plug-in exports an object. When instantiating the object in js, a callback function needs to be initialized. When the run function is called, the callback is called. I don't know where the problem is. How can I fix the code
#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)
my node version
λ node -v
v14.15.4
Upvotes: 1
Views: 1311
Reputation: 2248
move Eternal initialization to the constructor
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);
}
}
Upvotes: 0
Reputation: 40501
As I told you in the last question you asked: don't store pointers to stack-allocated objects. This has nothing to do with V8 or contexts.
In function New
, Eternal<Function> e_cb
is a stack-allocated object. The constructor call new MyClass(..., &e_cb)
creates and passes along a pointer to this stack-allocated object. But as soon as that function returns, all its stack-allocated objects will get torn down, so the pointer will then point to an invalid stack slot (likely reused and hence filled with "random" data). Of course, the same is true for context_
, but you're not using that anywhere, so the fact that it's broken is invisible.
In this specific case, you can simply pass the Eternal
by value instead, or create it in the MyClass
constructor. In general, I recommend reading up on C++ basics.
As for getting the "correct" context: what's wrong with Isolate::GetCurrentContext()
, which you are already using?
Upvotes: 1