node/test/addons/worker-addon-exit/binding.cc
Mohamed Akram be7ea272e3 test: add regression test for using ObjectWrap in worker
This content is taken from https://github.com/nodejs/node/pull/63575.
The original commit message is preserved below:

---

worker: fix premature addon unload

Weak callbacks could run after addons were unloaded, leading to a crash.

Signed-off-by: Mohamed Akram <mohd.akram@outlook.com>
PR-URL: https://github.com/nodejs/node/pull/63642
Fixes: https://github.com/nodejs/node/issues/63540
Refs: https://github.com/nodejs/node/pull/63575
Reviewed-By: Chengzhong Wu <legendecas@gmail.com>
2026-06-05 12:53:44 +00:00

99 lines
2.9 KiB
C++

#include <node.h>
#include <node_object_wrap.h>
using v8::Context;
using v8::Function;
using v8::FunctionCallbackInfo;
using v8::FunctionTemplate;
using v8::Isolate;
using v8::Local;
using v8::Number;
using v8::Object;
using v8::ObjectTemplate;
using v8::String;
using v8::Value;
class MyObject : public node::ObjectWrap {
public:
static void Init(v8::Local<v8::Object> exports);
~MyObject();
private:
explicit MyObject(double value = 0);
static void New(const v8::FunctionCallbackInfo<v8::Value>& args);
static void PlusOne(const v8::FunctionCallbackInfo<v8::Value>& args);
double value_;
};
MyObject::MyObject(double value) : value_(value) {}
MyObject::~MyObject() {}
void MyObject::Init(Local<Object> exports) {
Isolate* isolate = Isolate::GetCurrent();
Local<Context> context = isolate->GetCurrentContext();
Local<ObjectTemplate> addon_data_tpl = ObjectTemplate::New(isolate);
addon_data_tpl->SetInternalFieldCount(1); // 1 field for the MyObject::New()
Local<Object> addon_data =
addon_data_tpl->NewInstance(context).ToLocalChecked();
// Prepare constructor template
Local<FunctionTemplate> tpl = FunctionTemplate::New(isolate, New, addon_data);
tpl->SetClassName(String::NewFromUtf8(isolate, "MyObject").ToLocalChecked());
tpl->InstanceTemplate()->SetInternalFieldCount(1);
// Prototype
NODE_SET_PROTOTYPE_METHOD(tpl, "plusOne", PlusOne);
Local<Function> constructor = tpl->GetFunction(context).ToLocalChecked();
addon_data->SetInternalField(0, constructor);
exports
->Set(context,
String::NewFromUtf8(isolate, "MyObject").ToLocalChecked(),
constructor)
.FromJust();
}
void MyObject::New(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
Local<Context> context = isolate->GetCurrentContext();
if (args.IsConstructCall()) {
// Invoked as constructor: `new MyObject(...)`
double value =
args[0]->IsUndefined() ? 0 : args[0]->NumberValue(context).FromMaybe(0);
MyObject* obj = new MyObject(value);
obj->Wrap(args.This());
args.GetReturnValue().Set(args.This());
} else {
// Invoked as plain function `MyObject(...)`, turn into construct call.
const int argc = 1;
Local<Value> argv[argc] = {args[0]};
Local<Function> cons = args.Data()
.As<Object>()
->GetInternalField(0)
.As<Value>()
.As<Function>();
Local<Object> result =
cons->NewInstance(context, argc, argv).ToLocalChecked();
args.GetReturnValue().Set(result);
}
}
void MyObject::PlusOne(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
MyObject* obj = ObjectWrap::Unwrap<MyObject>(args.This());
obj->value_ += 1;
args.GetReturnValue().Set(Number::New(isolate, obj->value_));
}
void InitAll(Local<Object> exports) {
MyObject::Init(exports);
}
NODE_MODULE(NODE_GYP_MODULE_NAME, InitAll)