Skip to content

Commit 444c03f

Browse files
mohd-akramaduh95
authored andcommitted
test: add regression test for using ObjectWrap in worker
This content is taken from #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: #63642 Fixes: #63540 Refs: #63575 Reviewed-By: Chengzhong Wu <legendecas@gmail.com>
1 parent 7ac3fe1 commit 444c03f

4 files changed

Lines changed: 143 additions & 0 deletions

File tree

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
#include <node.h>
2+
#include <node_object_wrap.h>
3+
4+
using v8::Context;
5+
using v8::Function;
6+
using v8::FunctionCallbackInfo;
7+
using v8::FunctionTemplate;
8+
using v8::Isolate;
9+
using v8::Local;
10+
using v8::Number;
11+
using v8::Object;
12+
using v8::ObjectTemplate;
13+
using v8::String;
14+
using v8::Value;
15+
16+
class MyObject : public node::ObjectWrap {
17+
public:
18+
static void Init(v8::Local<v8::Object> exports);
19+
~MyObject();
20+
21+
private:
22+
explicit MyObject(double value = 0);
23+
24+
static void New(const v8::FunctionCallbackInfo<v8::Value>& args);
25+
static void PlusOne(const v8::FunctionCallbackInfo<v8::Value>& args);
26+
27+
double value_;
28+
};
29+
30+
MyObject::MyObject(double value) : value_(value) {}
31+
32+
MyObject::~MyObject() {}
33+
34+
void MyObject::Init(Local<Object> exports) {
35+
Isolate* isolate = Isolate::GetCurrent();
36+
Local<Context> context = isolate->GetCurrentContext();
37+
38+
Local<ObjectTemplate> addon_data_tpl = ObjectTemplate::New(isolate);
39+
addon_data_tpl->SetInternalFieldCount(1); // 1 field for the MyObject::New()
40+
Local<Object> addon_data =
41+
addon_data_tpl->NewInstance(context).ToLocalChecked();
42+
43+
// Prepare constructor template
44+
Local<FunctionTemplate> tpl = FunctionTemplate::New(isolate, New, addon_data);
45+
tpl->SetClassName(String::NewFromUtf8(isolate, "MyObject").ToLocalChecked());
46+
tpl->InstanceTemplate()->SetInternalFieldCount(1);
47+
48+
// Prototype
49+
NODE_SET_PROTOTYPE_METHOD(tpl, "plusOne", PlusOne);
50+
51+
Local<Function> constructor = tpl->GetFunction(context).ToLocalChecked();
52+
addon_data->SetInternalField(0, constructor);
53+
exports
54+
->Set(context,
55+
String::NewFromUtf8(isolate, "MyObject").ToLocalChecked(),
56+
constructor)
57+
.FromJust();
58+
}
59+
60+
void MyObject::New(const FunctionCallbackInfo<Value>& args) {
61+
Isolate* isolate = args.GetIsolate();
62+
Local<Context> context = isolate->GetCurrentContext();
63+
64+
if (args.IsConstructCall()) {
65+
// Invoked as constructor: `new MyObject(...)`
66+
double value =
67+
args[0]->IsUndefined() ? 0 : args[0]->NumberValue(context).FromMaybe(0);
68+
MyObject* obj = new MyObject(value);
69+
obj->Wrap(args.This());
70+
args.GetReturnValue().Set(args.This());
71+
} else {
72+
// Invoked as plain function `MyObject(...)`, turn into construct call.
73+
const int argc = 1;
74+
Local<Value> argv[argc] = {args[0]};
75+
Local<Function> cons = args.Data()
76+
.As<Object>()
77+
->GetInternalField(0)
78+
.As<Value>()
79+
.As<Function>();
80+
Local<Object> result =
81+
cons->NewInstance(context, argc, argv).ToLocalChecked();
82+
args.GetReturnValue().Set(result);
83+
}
84+
}
85+
86+
void MyObject::PlusOne(const FunctionCallbackInfo<Value>& args) {
87+
Isolate* isolate = args.GetIsolate();
88+
89+
MyObject* obj = ObjectWrap::Unwrap<MyObject>(args.This());
90+
obj->value_ += 1;
91+
92+
args.GetReturnValue().Set(Number::New(isolate, obj->value_));
93+
}
94+
95+
void InitAll(Local<Object> exports) {
96+
MyObject::Init(exports);
97+
}
98+
99+
NODE_MODULE(NODE_GYP_MODULE_NAME, InitAll)
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
'targets': [
3+
{
4+
'target_name': 'binding',
5+
'sources': [ 'binding.cc' ],
6+
'includes': ['../common.gypi'],
7+
}
8+
]
9+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
'use strict';
2+
const common = require('../../common');
3+
const { isMainThread, Worker } = require('worker_threads');
4+
5+
if (!isMainThread) {
6+
const addon = require(`./build/${common.buildType}/binding`);
7+
8+
9+
// Create some garbage
10+
const arr = [];
11+
for (let i = 0; i < 1e5; i++) arr.push(`${i}`.repeat(100));
12+
13+
new addon.MyObject(10);
14+
15+
// Should not segfault
16+
throw new Error('exit');
17+
} else {
18+
const worker = new Worker(__filename);
19+
worker.on('error', common.mustCall());
20+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
'use strict';
2+
const common = require('../../common');
3+
const { isMainThread, Worker } = require('worker_threads');
4+
5+
if (!isMainThread) {
6+
const addon = require(`./build/${common.buildType}/binding`);
7+
8+
new addon.MyObject(10);
9+
10+
// Should not segfault
11+
throw new Error('exit');
12+
} else {
13+
const worker = new Worker(__filename);
14+
worker.on('error', common.mustCall());
15+
}

0 commit comments

Comments
 (0)